🐸

테스트를 그만둔 내가 다시 테스트를 찾은 이유

Created
6/24/2021, 2:34:00 PM
Tags
테스트
ETC
Subtitle
다시 테스트
지금으로부터 약 1년 전, 맹목적으로 테스트 작성에 미쳐있었던 시기가 있었다. 커버리지를 채우기 위해 야근은 당연하고, 주말에도 하루 종일 테스트를 작성하여 테스트가 없었던 기존 서비스에 커버리지 88% 까지 채웠다.
그리고 1년이 지난 지금 커버리지 상태는 이러하다. 헤헷.

테스트 작성. 과연 합리적인가?

테스트는 이롭다. 아무리 테스트에 부정적인 사람이라도, 공짜로 코드를 준다면 굳이 마다할 이유가 없다.
다만 이것이 합리적이지 않다고 판단하는 조직은 테스트를 작성하지 않을 것이라 생각한다.
테스트를 작성할 당시에 나는 이 일이 합리적이어서 작성하진 않았다. 그냥 그 때 당시에 그게 유행이었다. TDD, BDD, 애자일과 같은 여러가지 방법론들이나 로버트 마틴, 마틴 파울러등의 거장들의 도서를 읽는 것들이 괜히 나를 전문 개발자로 느끼게 해주었기 때문이다. 문제에 대해 충분히 공감하고 필요에 의해 사용한 것이 아니다 보니 언제 관둬도 이상하지 않을 상황이였다.
그러니 어느 날 깨닫게 된 것이다. 이 짓거리를 왜 하고 있는 지를 말이다. 그 때 생각을 정리하자면 다음과 같다.
1.
기획/운영팀에서는 항상 빨리 제품이 나오는 게 중요하다.
2.
그런데 테스트를 작성하는데 시간이 꽤 걸린다.
3.
커버리지를 위해 결과가 뻔한 코드들도 굳이 작성해야 한다. 근데 그게 상당한 양이다.
4.
가끔은 테스트를 위해 운영 코드를 수정해야 한다. 근데 그것이 또 까다롭다.
5.
테스트 코드나 환경도 말썽을 피운다. 쉽사리 테스트하기 어려운 코드들도 분명 만나게 된다.
6.
요구 사항이 바뀌면 어렵게 만든 테스트를 싹 다 고쳐야 한다.
7.
물론 유닛 테스트가 에러를 잡아주기도 하지만 아주 가끔 이다. 그러니까 아주 가끔을 위해 2~6번을 해야 한다.
8.
팀 내에서 일부만 테스트를 작성 한다면 장독에 물 붓는 것처럼 아무리 테스트를 작성해도 커버리지는 떨어지게 된다.
9.
그러고 보니 우리 팀에는 QA가 존재한다.
10.
기획/운영팀에서는 항상 빨리 제품이 나오는 게 중요하다.
이 만큼 이유가 생기니 더 이상 테스트를 작성하지 않는 게 합리적이라고 결론을 지을 수 있었던 거 같다. 그리고 이 이후에도 그럭저럭 아무런 문제가 없었고, 가끔 예상치 못한 에러가 발생해도 운영이슈로 처리하면 될 문제였다.
운영이슈로 에러를 처리하는게 발생할지 어떨지도 모르는 에러를 예방 하는 것 보다 저렴했다.

실패한 테스트

그러니까 문제는 테스트를 위해 지불할 비용(시간)이 비싸다는 것이다.
팀원들이 테스트를 작성하지 않는 이유도 작성하기 싫고 귀찮고 까다로운 것이기 때문이다.
우리는 너무 비싼 작업 시간을 주고 코드 안전성을 가져왔다.
수지타산에 맞지 않는 트레이드 오프를 진행 했기 때문에 그 거래는 완전히 실패한 것이다.
테스트는 그 자체로 비합리적인게 아니다. 팀 내에 테스트를 적용하는 것이 실패했기 때문에 비합리적이게 된 것이다.
이제부터 조금 더 합리적으로 테스트를 할 수 있도록 고민해보자.

린 테스트를 위한 설계 ( 가장 쉽게 작성 하라)

테스트 코드는 제품 코드와 다릅니다. 단순하고, 짧고, 추상화가 없고, 무난하고, 작업하기에 편리하고, 린하게 디자인 하십시오. 테스트를 보고 즉시 의미를 알아챌 수 있어야 합니다. 우리 머리속은 제품 코드로 가득하고 부가적인 복잡한 것들을 생각할 여유가 없습니다. 또 다른 어려운 코드를 억지로 생각해내려고 한다면, 팀의 속도를 늦추게 되어 우리가 테스트를 하는 이유가 무색해 질 것입니다. 실제로 많은 팀들이 이런 이유를 테스트를 포기합니다. 테스트는 친절하고 웃는 동료와 함께 일하는 것이 즐거울 수 있는 기회이고, 적은 투자로 큰 가치를 제공하는 것입니다.
테스트를 작성하다 보면 테스트를 위해서 그 전에 준비해야 하는 많은 Context객체 들이 존재할 수 있다.
이전의 우리는 엄격하게 테스트를 하기 위해 매번 그러한 객체들을 생성해 왔다. 만약 이러한 객체를 만드는데 있어서 구조적인 문제가 있다면, 제품 코드를 고쳐왔다.
아래 예시를 살펴보자.
BAD CASE
test('한 명의 유저를 조회한다.', ()=>{ const userController = createUserController(); const userModel = UserModel.create(); const user = userController.getUser(userModel.id); assert(user).toEqual(userModel); }) const createUserController = () => { let currentUser = null; const authController = new AuthController({ get currentUser(){ return currentUser; } }); const userController = new UserController({ authController }) currentUser = userController.buildCurrentUser(); return userController }
JavaScript
UserController를 생성하려면 authController가 필요하다.
AuthContollercurrentUser를 필요로 하는데, 이는 UserController의 public 함수로 생성할 수 있다.
지독한 순환참조때문에 이 문제를 해결하기 위해 AuthControllercurrentUser대신에 getter를 넘겨주어서 lazy하게 판단하게 한다.
일단 제품코드에서 순환참조(Circular Dependencies)를 해결하는게 최선의 방법일 것은 당연하지만, 이번엔 제품 코드에 대해서 이야기 하려는 것은 아니다.
이 구조를 고치기 위해선 상당한 시간이 필요할 지도 모르는 일이고 팀이 공감하지 않는 채 혼자 기능을 수정할 수도 없는 노릇이다. 그렇다고 테스트를 포기할 수는 없는 상황이다.
결국 방법은 생각해 냈지만, 쉽지 않은 구조이고 이로 인해 많은 에너지를 쏟아부어야 했다. ( 그리고 앞으로도 매번 Controller를 위해 생성 코드를 만들어 내야 한다. )
나는 이러한 방법이 좋지 않다고 생각한다. 특히 아직 팀에 테스트가 녹아들지 못한 상황이라면 더 더욱 말이다.
팀 차원에서 테스트의 거부감이 생기면 시작조차 못한다. 테스트가 팀원들에게 스트레스를 줘서는 안된다.
수정한 코드는 다음과 같다.
GOOD CASE
test('한 명의 유저를 조회한다.', ()=>{ const { userController } = createMockRequestContext(); const userModel = UserModel.create(); const user = userController.getUser(userModel.id); assert(user).toEqual(userModel); }) const createMockRequestContext = () => { let currentUser = new User(); const authController = new AuthController({ currentUser }); const userController = new UserController({ authController }) currentUser = userController.buildCurrentUser(); return { userController, authController, currentUser } }
JavaScript
어떤 Controller도 사용할 수 있게 RequestContext를 생성했다.
currentUser도 필요하지 않아 정적으로 전달했다. 만약 필요하다면 인자로 전달하면 된다.
더 쉽게 작성하기 위해서 호출, 검증하기 힘든 코드라면 하드코딩을 해서라도 좋다.
또한 테스트 더블과 같은 도구들을 적극적으로 사용하자.
< js 테스트 더블 라이브러리인 sinon >
추상화, 유연성보다 쉽고 빠른 길을 택하자.

한글로 써라. 오로지 커뮤니케이션을 위해.

테스트명을 작성하는 것 역시 충분히 피로감을 준다. 또한 기껏 고민해서 작성하더라도 개개인이 취향에 따라 작성한 메시지는 한 눈에 읽히지 않는다.
결국 테스트가 문서로써 기능을 하려면 팀 차원에서 컨벤션을 정하는 것이 좋다.
이는 위의 javascript-testing-best-practices에 이미 좋은 방법들이 있으니, 기호에 맞게 사용하면 좋을 듯 하다.
다만 여기서 추가적으로 공유하고 싶은 내용은 이 테스트를 영어로 써야하는지에 대한 이야기이다.
테스트를 작성하는 장소가 여러가지 있을 것이다. 개인 프로젝트 혹은 오픈소스 라이브러리등 말이다.
그런데 만약 산업군에 있다면, 그러니까 일로써 테스트를 작성한다면 한글로 작성 하는 것을 권장한다.
제품 개발자로써 오로지 회사의 이익만을 위해 고민한다면 그 이유가 있다.
우리가 테스트를 100% 활용하여 이를 기술 문서로써도 가치있게 만들려면, 누가 봐도 이해할 수 있어야 하는 것이 중요하다. 다음 주에 오는 신입 사원이 한국어를 모르는 경우와, 영어를 모르는 경우 어느 쪽이 더 높은가?
커밋 메시지를 비롯하여 나는 꽤 오랫동안 영어로 모든 것을 작성 해왔다. 테스트도 마찬가지이다. 영어로 익숙해지면 좋지 않겠냐는 생각에서 사용했다. 뭐든 자주 쓰고 익숙해져야 실력이 느니까.
그런데 이러한 생각은 적어도 프로페셔널하지 않다. 회사이지 학원은 아니지 않는가? 겸사겸사 성장하는것이야 물론 환영하지만 모든 것은 제품을 위해서 가장 최선의 방법만을 생각해야 한다.
BAD CASE
describe("User Test",()=> { describe("If request have user permission", ()=> { test("I can get a user information.", ()=>{ }) }) }) // 최악의 경우에는 번역기를 열심히 돌려 어렵게 작성했는데 // 잘못된 문법이나 표현일 수도 있다는 것이다.
JavaScript
GOOD CASE
describe("유저 테스트",()=> { describe("만약 권한이 있다면", ()=> { test("유저 정보를 조회할 수 있다.", ()=>{ }) }) })
JavaScript
영어로 에너지 쏟지 말자. 다시 한 번 말하지만 테스트는 간단 해야한다.

"TDD는 판타지다"라고 말했던 내가 TDD를 하고 있는 이유

제가 판타지라고 생각하는게 딱 두가지 있거든요? 하나는 애자일이고, 다른 하나는 TDD입니다. 사람 많은 대기업에서나 가능한거 아닐까요?
코린이 시절, 마틴 파울러와 같은 거장들에 말에 거하게 취해서 TDD와 사랑에 빠졌다가 이별에 쓴 맛을 본 내가 입에 달고 살았던 말 중에 하나이다.
TDD의 장점을 구글에 다시 한 번 검색해본다.
1. 피드백이 빠르다. 2. 변화에 대한 두려움을 줄여준다. (리팩토링이 편하다) 3. TDD를 하면 코드 복잡도가 떨어진다. 4. 디버깅 시간을 줄여준다. 5. 동작하는 문서 역할을 한다.
JavaScript
저런 마법같은 일이 일어난다니... 여전히 나에게는 판타지다.
하지만 우리 팀엔 TDD가 필요하다.

우리는 게으르다. 생각보다 훨씬 상상을 초월할 정도로 게으르다.

테스트가 필요하다는 것은 팀원들의 공감대를 형성하였다. 그래서 어떻게 하면 좋을지 회의를 한 번 가졌었는데, 회의를 준비하면서 나는 우리가 TDD를 선택할 것이라고는 조금도 생각하지 못했다. 솔직히 지금도 놀랍다.
결론까지 도출되기 위해 다음과 같은 대화가 오갔다.
그렇다. 애초에 먼저 작성하면 피할 수 없다는 단순한 메커니즘에 의해 TDD로 도착하게 되었다.
요즘 특히 이런 팀들에게 있어서 테스트 작성을 하기 위해서 TDD가 유일한 돌파구이지 않나? 라는 생각을 한다.
오로지 두 개의 선택지만 있는거다.
테스트 하지 않는다.
TDD를 한다.
다만 이 점은 아직 팀에 적용중이기 때문에 좀 더 지켜보면 더 좋은 대답을 할 수 있을꺼 같다.

계속 진화하라.

지금까지 쉽고 단순하게 작성하라고 말해왔지만, 이는 쉬운 일이 아니다.
테스트에 대해 많은 인사이트들을 조사해왔지만, 알면 알수록 어렵다. 딱 떨어지는 정답도 없다.
이전 웹 개발자도 수학을 배워야 할까 에서와 같이 테스트는 훈련의 영역이다.
프레임워크 학습과 같이 공부한다고 해서, 어느 날 갑자기 할 수 없는 것들을 할 수 있게 되지 않는다.
꾸준히 공부하고 고민하고 좋은 방향으로 변해가야 좋아진다. 생각 없이 기계적으로 작성하면 테스트는 어느 날 이전처럼 가성비 좋은 도구가 아닌 값 비싼 도구가 될 수 있다. 수 많은 도구들을 탐구하고 그 효용을 높여라.
아래와 같이 좋은 예제와 연습들이 이미 많다.
이 외에 팀에 적용하면서 봤던 나머지 좋은 자료들 역시 아래에 첨부한다.

마지막으로

끊임없이 정진하라.