코드를 짤때 항상 로직부터 구현하고 테스트 코드를 짜온것 같다. 그 방법에 대한 문제점과 해결방안을 알아보자
관측할 때마다 다른 값에 의존하는 코드(현재날짜/시간/랜덤 값/전역변수)는 외부에서 받도록 하자
외부 세계에 영향을 주는 코드(데이터베이스기록,메시지발송..)
같은 입력에는 항상 같은 결과
외부 세상과 단절된 형태
테스트 하기 쉬운 코드
프로덕션 기능을 설명하는 테스트 코드 문서
다양한 테스트 케이스를 통해 프로덕션 코드를 이해하는 시각과 관점을 보완
테스트 행위에 대한 결과까지 기술한다.
명사의 나열보다는 문장으로 표기하자
도메인 용어를 사용하여 한층 추상화된 내용 담기
테스트 현상을 중점으로 기술하지 말것
프로덕션 코드보다 테스트 코드를 먼저 작성하여 테스트가 구현과정을 주도하도록 하는 방법론
최대한 빠르게 초록불을 보도록한다 그리고 나서 리팩토링
테스트 자체의 누락 가능성
특정 테스트 케이스만(해피케이스)만 검증할 가능성
잘못된 구현을 다소 늦게 발견할 가능성
복잡도가 낮은, 테스트 가능한 코드로 구현할 수 있게 한다.
쉽게 발견하지 어려운 엣지 케이스를 놓치지 않게 해준다.
구현에 대한 빠른 피드백을 받을 수 있다.
과감한 리팩토링 가능
TDD에서 파생된 기법
함수에 초점을 맞춘 테스트보다는 시나리리오에 기반한 테스트케이스(TC)자체에 집중하여 테스트
given/when/then
data access 역활
비즈니스 가공로직 포함되면 안된다. data에 대한 crud에만 집중한 레이어
persistence Layer와의 상호작용을 통해 비즈니스 로직을 전개 시킨다
트랜잭션을 보장해야 한다.
외부 셰계의 요청을 가장 먼저 받는 계층
파라미터에 대한 최소한의 검증을 수행한다.
하위에 있는 두 레이어는 mocking 처리 , WebMvcTest를 사용해 컨트룰러 관련 빈들만 올려주자
@DataJpaTest : Jpa관련된 빈들만, 롤백 있음
@SpringBootTest : 롤백 없음, 클렌져 만들어 주던가 아니면 트랜잭션 애노테이션 붙어야함
@Transactional 이 붙은 testCode가 통과하면 프로덕션 코드에도 있다고 착각할 수 있으니 주의하자!!
@Transactional에서 read= only 여부에 따라 MasterDB에 보낼지 slaveDB에 보낼지 DB 엔드포인트 구분 가능
가짜 객체
먹처리 -> 테스트할때 의존관계에 있는것들을 가짜로 처리한다. 그래서 테스트 하려하는 레이어에 좀 더 집중할 수 있음
컨테이너에 mock으로 만든 객체를 넣어준다.(Spring context)
Presentation Layer 테스트시 (@WebMvcTest) service 빈들은 등록 안됨 그래서 가짜로 마들어줄 필요가 있다.(@MockBean)
엔티티에 있는 Validation 체크는 service 단에서 체크 x
Controller entity -> DTO -> ServiceDTO -> Service 로 Service DTO로 한번더 변환 시켜주자 그렇지 않으면 의존성 @Valid를 계속 Service단에 들고 가야하기 때문
service에서 컨트룰러 DTO에 의존하지 않음으로써 의존성 분립
큰단위로 테스트 만들면서 필요한 작은단위(메서드) 테스트 코드를 작성해 준다.
- Dummy : 아무것도 하지 않는 깡통 객체
- Fake : 단순한 형태로 동일한 기능은 수행하나, 프로덕션에서 쓰기에는 부족한 객체
- Stub : 테스트에서 요청한 것에 대해 미리 준비한 결과를 제공하는 객체(상태검증)
- Spy : Stub이면서 호출된 내용을 기록하여 보여줄 수 있는 객체
- Mock : 행위에 대한 기대를 명세하고, 그에 따라 동작하도록 만들어진 객체(행위검증)
실제 객체를 기반으로 만들어짐 (Stubbing 사용시 do 사용)
일부는 Mocking처리 Stubbing을 사용하고 일부는 실제 객체 로직 사용하고 싶을때
given 절인데 when 문법을 사용하고 있음. 이를 고치려고 나온게 BDDMockito
mockito를 bdd로 한번 감싼것, 기능은 동일
한문단 한주제
- if문과 for문 두개로 친다. 읽는 사람이 생각해야하므로 최대한 지양/ 최대한 논리구조 사용 x
- 테스트 코드는 볼때 정말 쉽게 읽어지도록 만들기 -> 쉽게 여러개 만들기, 한번에 테스트하려 x
완벽하게 제어하기
- 제어할수 없는 값은 상위레벨로 올리기, 현재 시간은 쓰지말자
테스트 환경 독립성 보장
- 2가지 이상 행위가 들어가지 않아야함
- 테스트 환경은 최대한 생성자 기반으로!(팩토리메서드 말고 순수생성자로) -> 팩토리메서드도 '목적'이 들어간 구문이기 때문
- 테스트 코드 실패이유가 주행위가 아닌 서브 행위인경우
- 맥락을 이해하려는데 허들이 있으면 안됌! 최대한 직관적으로!!
테스트간 독립성 보장
- 공유자원 금지
TestFixture 구성하기
- given절이 공통된다고 @BeforeEach에 넣지말자, 이 영역에는 아예 몰라도 테스트 내용을 이해하는데 문제가 없는것, Or 수정해도 모든 테스트에 영향을 않는것만 넣어주자
- 빌더로 given절 생성시 필요한 파라미터만 보내주자 그래야 테스트시 뭐를 볼지 한눈에 들어옴
TexstFixturer 클렌징
- deleteAll -> 연관된 테이블을 selecet하고 for문으로 건건히 지움
- deleteAllInBatch -> select과정이 없고 통째로 delete시켜버림.
private 메서드
- public 메서드 테스트를 할때 private 메서드 또한 포함되어 있으니 자동으로 검증되서 따로 할필요 없다.
- 반드시 private 메서드를 테스트 해야할 경우 메서드를 따로 분리해 객체를 따로만들어 public으로 만든후 테스트 진행
- private 메서드 테스트가 필요한경우 이 메서드가 너무 많은 기능을 수행 하지는 않는가, 객체를 분리해야 하는가 고민 필요