본문 바로가기
노트/Review

[서적] 테스트 주도 개발 시작하기 | 최범균

by soro.k 2023. 3. 11.

 

테스트 주도 개발 시작하기 | 최범균 - 교보문고

테스트 주도 개발 시작하기 | 작동하는 깔끔한 코드를 만드는 데 필요한 습관 - JUnit 5를 이용한 테스트 주도 개발 안내 - 테스트 작성과 설계를 위한 대역 - 테스트 가능한 설계 방법 안내 - 유지

product.kyobobook.co.kr

 

작년부터 테스트 코드에 관심이 생기기 시작했는데 이번 프로젝트에서 본격적으로 써보기 전에 제대로 된 서적 하나는 읽어야겠다 싶어서 주변 개발자 분께 추천을 받아서 읽게 된 책이다. 이 책을 사기 전에 <단위 테스트>라는 책의 두 챕터 정도를 먼저 읽었는데 그때는 모호했던  개념들이 이 책을 읽고 나서 다시 보니 조금 더 정리가 됐다. 

 

TDD를 썼을 때 개발자는 어떤 이점을 경험할 수 있는지와 테스트의 범위 그리고 테스트 대역 객체들을 어떨 때 써야하는지에 대해 배우고 싶다면 이 책을 보면 좋을 것 같다. 그리고 유지 보수하기 좋은 테스트 코드와 좋지 않은 예도 보여주기 때문에 테스트 코드를 처음 작성하는 사람들도 이해하기 쉬울 것 같다.

 


TDD란 무엇인가

기능을 검증하는 테스트 코드를 먼저 작성하고 테스트를 통과시키기 위해 개발을 진행하는 방법이다.

 

TDD Cycle (Red - Green - Refactor의 반복)

테스트를 먼저 작성하고 테스트를 통과시킬 만큼 코드를 작성하고 리팩토링으로 마무리하는 과정을 반복한다.
  1. 기능을 검증하는 테스트를 먼저 작성한다.
  2. 작성한 테스트를 통과하지 못하면 테스트를 통과할 만큼만 코드를 작성한다.
  3. 테스트를 통과한 뒤에 개선할 코드가 있다면 리팩토링한다.
  4. 다시 테스트를 실행해서 기존 기능이 그대로 동작하는지 확인한다.
  5. 1-4를 반복하여 기능을 완성한다.

 

장점

  • 개발 과정에서 지속적으로 코드 정리를 하기 때문에 코드 품질이 급격히 나빠지지 않게 막을 수 있으므로 유지보수 비용을 낮춘다.
  • 새로운 코드를 추가하거나 기존 코드를 수정할 때 테스트를 이용해서 제대로 동작하는지 바로 확인할 수 있으므로 잘못된 코드가 배포되는 것을 방지한다.

 

대역의 필요성

테스트를 작성하다 보면 외부 요인이 필요한 시점이 있다.

  • 테스트 대상에서 파일 시스템 사용
  • 테스트 대상에서 DB로부터 데이터를 조회하거나 데이터를 추가
  • 테스트 대상에서 외부의 HTTP 서버와 통신

이럴 때 대역 클래스를 만들어서 실제 외부 API와 연동하지 않고도 테스트할 수 있게 할 수 있다.

 

대역의 종류

대역 종류 설명
스텁(Stub) 구현을 단순한 것으로 대체한다. 테스트에 맞게 단순히 원하는 동작을 수행한다. StubCardNumberValidator가 스텁 대역에 해당한다.
가짜(Fake) 제품에는 적합하지 않지만, 실제 동작하는 구현을 제공한다. DB 대신에 메모리를 이용해서 구현한 MemoryAutoDebitInfoRepository가 가짜 대역에 해당한다.
스파이(Spy) 호출된 내역을 기록한다. 기록한 내용은 테스트 결과를 검증할 때 사용한다. 스텁이기도 하다.
모의(Mock) 기대한 대로 상호작용하는지 행위를 검증한다. 기대한 대로 동작하지 않으면 익셉션을 발생할 수 있다. 모의 객체를 스텁이자 스파이도 된다.

 

주의할 점

모의 객체를 과하게 사용하지 말자.

  • 모의 객체를 이용하면 대역 클래스를 만들지 않아도 되니까 처음에는 편할 수 있다. 하지만 결과 값을 확인하는 수단으로 모의 객체를 사용하기 시작하면 결과 검증 코드가 길어지고 복잡해진다.
  • 모의 객체는 기본적으로 메서드 호출 여부를 검증하는 수단이기 때문에 테스트 대상과 모의 객체 간의 상호 작용이 조금만 바뀌어도 테스트가 깨지기 쉽다.
  • 모의 객체의 메서드 호출 여부를 결과 검증 수단으로 사용하는 것은 주의해야 한다.
  • 특히 DAO나 리포지토리와 같이 저장소에 대한 대역은 모의 객체를 사용하는 것보다 메모리를 이용한 가짜 구현을 사용하는 것이 테스트 코드 관리에 유리하다.

 

테스트 범위

단위 테스트

  • 단위 테스트는 개별 코드나 컴포넌트가 기대한대로 동작하는지 확인한다.
  • 한 클래스나 한 메서드와 같은 작은 범위를 테스트한다. 일부 의존 대상은 스텁이나 모의 객체 등을 이용해서 대역으로 대체한다.

 

통합 테스트

  • 시스템의 각 구성 요소가 올바르게 연동되는지 확인한다.
  • 기능 테스트는 앱을 통해 가입 기능을 테스트한다면 통합 테스트는 서버의 회원 가입 코드를 직접 테스트하는 식이다.
  • 회원 가입 관련된 서비스 클래스, DAO 인터페이스, SQL 쿼리를 구현했다면 이들을 통합한 회원 가입 서비스 클래스에 대한 테스트가 통합 테스트 예가 될 수 있다.
  • 스프링 프레임워크나 마이바티스 설정이 올바른지, SQL 쿼리가 맞는지, DB 트랜잭션이 잘 동작하는지 등을 검증할 수 있다.

 

기능 테스트

  • 기능 테스트는 사용자 입장에서 시스템이 제공하는 기능이 올바르게 동작하는지 확인한다.
  • 사용자가 직접 사용하는 웹 브라우저나 모바일 앱부터 시작해서 데이터베이스나 외부 서비스에 이르기까지 모든 구성 요소를 하나로 엮어서 진행한다.