테스트코드 작성 하시나요?


얼마 전 모임에서 만난 후배 개발자가 면접 경험을 공유했다. '면접관이 테스트 코드 작성 여부를 물었다'라고 했다. 나 역시 면접에서 이를 중요하게 여긴다.

면접에서 나는 테스트 코드 작성의 이유를 반드시 질문한다. 단순히 테스트를 어떻게 작성에 대한 하드스킬에 대한 질문의 의도가 아니다. 테스트를 정말 작성을 한다면 경험을 기반으로  한 필요성에 대해서 설명할 수 있다고 생각하기 때문이다. 
실제 업무에서 테스트코드 작성을 하게 된다면 업무 생상성뿐만이 아니라 좋은 코드 설계도 가능하다고 생각하기 때문이다.  왜  그렇게 생각하는지를 좀 정리를 해보려고 한다.


첫   번째 이유는 빠른 피드백이다. 예를 들어 회원가입 API를 만드는 Task 가 있다고 하자. 이메일 형식을 검증하는 요구사항이 있는데, POST /users API를 작성하면서 회원가입   시 이메일을 유효성 검사기능을 개발자 수준에서 테스트를 해야 할 것이다. 테스트 코드가 없다면 코드를 작성하고 API를 호출해서 테스트하고, 버그가 있다며 다시 수정하고 API 호출 테스트 해보고 이 사이클을 계속적으로 반복하게 된다. 아무리 빨라도 5초 정도 소요가 된다고 하자. 이를 테스트코드를 이용한다면 처음 작성이 시간이 걸리겠지만, 처음 이후에는 1s 도 안  걸릴 것이다. 만약 이런 상황이 누적된다면 생산성에 많은 기여를 할 것이다.


두  번째는 복잡한 요구사항을 표현할 수 있다.  프로그래밍을 하다가 if 문을 통해서 로직의 분기를 해야  하는 상황이 있다. 그런 상황은 십중팔구 비즈니스 요구사항이 들어간다.  “최소주문 금액보다 음식가격이 낮다면 주문 실패를 한다.”라는 요구사항을 위해 분기문을 사용할 수밖에 없다.  이런 의도를 테스트코드로 작성을 하고 누군가가 해당 코드를 리팩토링 하다가 지우는 버그도 예방을 할 수가 있다.  그래서 나는 팀에서 Branch(Condition) Coverage를 관리를 한다.


세   번째는 자유로운 리팩토링이 가능하다.  테스트 코드를 작성하며 개발하는 도중, 한 선배 개발자가 프로젝트의 패키지 구조를 대대적으로 변경한 적이 있다. 당연히 내가 작성한 코드에도 영향이 있었고, 처음에는 큰 변화에 대한 두려움이 있었다. 실제를 conflict를 스스로 해결해 보았는데, 테스트코드 때문에 오히려 심리적 안전하다는 생각이 들었다.  그래서 리팩토링의 첫  단계는 테스트코드라고 말하는 것이다. 


네  번째는 설계의 개선이 가능하다는 것이다. 테스트코드를 작성을 쉽게 하기  위해서는 유연한 의존성이 필수적이다. 자연스레 코어 비즈니스로직과 세부사항을 분리를 하게 된다. 단위 테스트 대상 클래스와 협력하는 클래스  간의 의존성이라 메시지 규약도 고려를 하게 된다.


아주 가볍게 내가 생각하는 이유들을 적어 보았다. 동의하는 부분도 있을  테고 동의하지 않는 부분도 있을 것이다. (다양한 의견 환영합니다.) 나도 이  글을 쓰면서 이 글을 읽으시는 분들에게 빠르게 피드백을 받고자 하는 목적도 있기 때문이다. 

빠른 피드백, 유연한 의존성, 그리고 리팩토링의 용이성을 갖추어 나가다 보면 어제 보다 나은 코드를 만들기 위한 프로덕트의 기본 바탕이  될 수 있지 않을까 생각한다.  다음에는 좀 더 구체적인 사례로 테스트 코드 작성을 어떻게 할 수 소개해 보도록 하겠다.


https://anyjava.tistory.com/136

다음 내용이 궁금하다면?

또는

이미 회원이신가요?

2023년 11월 13일 오전 12:13

 • 

저장 332조회 11,941

댓글 13

  • 의견을 나누고 싶어서 댓글 남깁니다. <테스트 코드가 정말 생산성에 기여하는가?> Unit Test를 위해 Service를 테스트하는 코드를 작성하고 있고, 일부 Service 간 Dependency가 걸려 있는 상황에서 Service Layer에 새로운 Service가 추가 될 때마다 테스트 코드가 변경되어야 합니다. 테스트 코드 내에서 새로 추가된 Service의 Mocking을 해주는 것 뿐만 아니라 Service 간의 Dependency에 의해 테스트 하려는 Service 내부에서 사용하는 다른 Service들의 Mocking도 해줘야 하므로 필연적으로 Business Logic을 보면서 테스트 코드를 작성해야 하는 상황이 올 수 있죠. 오히려 테스트를 용이하게 하기 위해서 테스트 코드를 작성하는게 아닌 테스트 코드를 작성해야 하기 때문에 테스트 코드를 작성하는 상황이 오는 경우가 생기게 됩니다. Controller를 테스트 하는 코드를 작성하는 경우에는 더 험난해질 수 있죠. 그래서 네번째에 말씀하신 것처럼 유연한 의존성을 위해 모듈화를 더 잘게 한다면 오히려 Business 로직이 복잡해지고 읽기 어려운 코드가 될 가능성이 생깁니다. 이 부분에 대해서는 어떻게 생각하시는지 궁금하네요.

    @K 말씀하신 단점이 없으면 테스트 코드 작성이 좋다는 말씀으로 보입니다. controller 에 대해서는 테스트 코드를 작성하지 않고 mocking 도 managed dependencies 에 대해 사용하지 않는다면 극복할 수 있는 문제 같습니다.

    @K 의견 감사합니다. 저도 비슷한 경험을 하면서 힘겹게 UnitTest 에 경험을 쌓아왔습니다. 한마디로 정리하면, Framework (Spring) 에 의존된 대상을 테스트하게 되면 어려워 지는 케이스가 많았습니다. 특히, Controller 의 경우 SpringMVC 기반이라면 완전한 SpringFramework 에 의존된 테스트가 되어서 skip 을 하게 되거나, 주요 SpringMVC 의 annotation 을 학습하는 용도로 테스트 작성합니다. 예를들어, Path Parameter 를 처음 사용하는데, 정말 path value 가 controller 의 parameter 로 주입이 되는지를 검증하죠. 간단하게 저의 기준을 말씀드리면, 1. mocking 은 최소화 합니다. (리팩토링시 지옥을 경험함...) 2. 추상화된 데이터타입(domain, dto, vo 등)을 활용하게 됩니다. 3. Servie Layer 는 비지니스 흐름 + transaction 경계로만 활용하고 Test 는 Read Transaction 은 선택적, Write Transaction 쪽은 인수테스트 형태로 작성중입니다. 항상 내가 테스트 할려고하는게 Framework 에 의존되지 않게 하는게 중요했던것 같네요. 좀 더 구체적인 사례를 앞으로 글을 통해서 계속 공유 드릴 예정입니다.

    @K 저도 개발 하다보면 가끔 말씀하신 비슷한 문제나 이런것까지 해야되나??싶은 경우도 많이 있는데요. 그래서 최근에는 단위 테스트는 줄이고 차라리 통합 테스트를 늘리는 방향으로 가고 있습니다. 아직 스타일을 바꾼지 얼마 안되서 실효성 확인은 잘 안됐지만 조금은 편해진 느낌입니다. 이후 유지보수건이 발생할때에나 실효성 확인이 될거 같아요.

    @K 비즈니스 로직에 스프링에 대한 의존성을 없애고 pojo 클래스로만 작성. mocking 은 언제 하는가 하면 상태 값 검증 보다 동작 검증을 위해 사용 하는게 좋다고 생각 합니다. 스프링에서의 controller, service 은 단지 값을 전달하고 불러오기 위한 코드만 존재하는거죠

    스프링 의존성을 최소화하려면 결국 각 클래스들을 인터페이스로 정의하고 테스트를 위한 스터빙이 필요해집니다. 결국 K님의 의견대로 테스트를 위해 비즈니스 로직이 더 복잡해지는 결과는 동일하게 나타나지 않을까요? 저도 K님과 동일한 의견이긴 하네요.

    @K 사실 모킹을 최소하하는건 문제가 있습니다. 서비스 자체가 굉장히 단순한 트랜잭션마누요구한다면 가능하지만 대부분의 경우 그렇지 못하고, nosql기준으로 여러 db 컬렉션에 접근하게 될 가능성이 높죠. 추상화를 하더라도 모킹을 해야되는건 마찬가지겠죠? 그렇다고 단위테스트를 버리고 E2E 테스트를 하자니 네트워크 고려 및 테스트 db를 사용하는 과정에서 생기는 테스트 자체가 느려지는 상황도 올 수 있구요. 사실 프레임워크에 의존하냐 안하냐의 문제보단 내 코드 내의 모듈화를 어떻게 가져가는지가 더 중요해 보여요. 모듈화된 함수들을 테스트하는게 원래의 목표였으니까요. 사실 점점 테스트 코드가 정말 이득인가? 하는 의문이 많이듭니다. 좋은 글 감사해요

  • 테스트코드가 일이되면 안되겠죠. . 특히 일정에 쫒기는 우리나라 현실에선 더욱 힘들다는 생각이 드네요

  • 저도 의견을 공유하는 차원에서 댓글을 남깁니다. 조직이 처한 상황에 맞게 적절한 수준으로 유닛테스트 코드를 작성하는 요령을 찾아가는 것도 중요하다고 생각합니다. 좋은 사례들이 많으나 이를 따르려다 보면 시간적인 혹은 기술적인 어려움으로 포기는 경우가 많은 것 같습니다. 커버리지가 높지 않더라도 유닛테스트 작성은 말씀해주시는 여러 장점이 있기에 도입하는 것 자체가 실익이 많습니다. 테스트코드 작성하려면 왠지 커버리지 100%를 달성해야 할 것 같고 외부서비스와 연관된 기능에 대해 Mock을 만들어야 할 것 같은 그런 압박이 느껴집니다. 기능 구현이 그렇든 현실적인 제약으로 완벽하게 못짜더라도 충분히 가치 있는 일이라고 생각합니다. 계속 시도하고 고민하다 보면 조직 상황에 맞게 적당한 수준으로 타협하는 요령이 생기는 것 같습니다. 이 또한 설계 능력을 키워 카듯 테스트 코드 설계 역량이 느는 거라서 본인의 자산(?)이 되는 거라 생각합니다.

  • 좋은글 잘 읽었습니다. 테스트 코드는 서비스 규모가 커질수록 그 가치는 어마어마 해집니디. 예를 들면 1천명 회원일 때는 테스트 코드가 실수를 예방해줘서 100만원 손실을 막아줬다면 100만 회원일 때는 10억 손실을 막아주는것과 같죠

  • 테스트 코드라는것은 뭘까요? 전체 코드에 적용해서 해보는것이 아닌 각 기능만을 나누어 구현하여 실험해 보는것을 말하는건가요?

    @이준식 전체 코드에 적용은 보통 e2e 테스트라 하고. unit test 라고 기능별로 테스트 코드를 작성해 놓습니다. 모든 경우에 유닛 테스트 코드를 작성한다는건 현실적으로 불가능하고, 단위 핵심 코어 기능에 테스트 코드를 넣어서 중요 코드를 보호하는 역활로 사용하기도 합니다. 프로그램의 사이즈가 커서 빌드할때 로딩할때 너무 오래 걸리는 경우, 테스트 코드로 개발해서 적용시켜 넣기더 하고요