테스트 코드의 구성요소
● 상황 == given
테스트 할 대상에 따라 상황을 설정하는 방법이 달라짐
● 실행 == when
● 결과 확인 == then
이 구조로 작성해야하는 것은 맞지만 이 구조에 너무 매몰되서도 안됨. 테스트 내용의 이해를 위해서 존재하는 것
우연에 의해 테스트 결과가 달라질 수 없도록 해야한다
ex) 파일이 없는 경우 테스트에서 현재 존재하지 않는 파일이름으로 리턴 → 명시적으로 파일이 없는 상황을 만들기
실제 존재하는 경우에도 삭제하는 로직 추가 → 항상 파일이 존재하지 않음 보장
외부 상태가 테스트 결과에 영향을 주지 않음 + 테스트 코드는 항상 정상적으로 동작하는 것이 중요함
외부 상태에 따라 테스트의 성공 여부가 바뀌지 않기 위한 방법
1. 테스트 실행 전에 외부를 원하는 상태로 만들기
2. 테스트 실행 후에 외부 상태를 원래대로 되돌려 놓기
하지만, 외부 상황은 테스트 코드에서 마음대로 제어할 수 없는 경우 존재
테스트 대상의 상황과 결과에 외부 요인이 관여할 경우 대역을 사용하면 테스트 작성이 용이해짐
● 대역 : 테스트 대상이 의존하는 대상의 실제 구현을 대신하는 구현 == test double
→ 이를 통해서 외부 상황이나 결과 대체 가능
외부 요인이 테스트에 주로 관여하는 경우
● 테스트 대상에서 파일 시스템 사용
● 테스트 대상에서 DB로 부터 데이터를 조회하거나 데이터 추가
● 테스트 대상에서 외부의 HTTP 서버와 통신
레포지토리의 경우 메모리를 이용해서 대역을 생성하여 테스트 작성 → 실제 DB를 사용하지 않고도 로직 검증가능
대역의 종류
| 대역 종류 | 설명 |
| 스텁 (Stub) | 구현을 단순한 것으로 대체. 테스트에 맞게 단순히 원하는 동작을 수행. |
| 가짜 (Fake) | 제품에는 적합하지 않지만, 실제로 동작하는 구현 제공. DB대신에 메모리 이용해서 구현한 경우 이에 해당. |
| 스파이 (Spy) | 호출된 내역 기록. 기록한 내용은 테스트 결과 검증 시 사용. 스파이는 스텁에 해당함. |
| 모의 (Mock) | 기대한 대로 상호작용하는 지 행위 검증. 기대한 대로 미동작시 익셉션 발생. 모의 객체는 스텁 & 스파이 모두에 해당 |
* 스파이 대역 사용 예
메일 발송 기능 테스트 할 경우, EmailNotifier이 발송 이메일을 사용하였는지 테스트하면 됨
모의 객체를 작성하는 방법 중 하나에 Mockito 존재
대역 객체가 기대하는 대로 상호작용 했는지 확인하는 것이 모의 객체의 주요 기능
제어하기 어려운 외부상황의 경우 ≫ 의존을 도출하고 이를 대역으로 대신함
의존 도출 방법
1. 제어하기 힘든 외부 상황을 별도 타입으로 분리
2. 테스트 코드는 별도로 분리한 타입의 대역을 생성
3. 생성한 대역을 테스트 대상의 생성자 등을 이용해서 전달
4. 대역을 이용해서 상황 구성
이렇게 하면, 당장 로직을 구현하지 않않아도 테스트 코드를 통과시킬 수 있음
대역 사용의 장점
- 대기 시간을 줄여줌 → 개발 속도 향상
- 실제 구현이 없어도 다양한 상황에 대한 테스트 및 실행 결과 확인 가능
모의 객체 남용시,
- 결과 검증 코드가 길어지고 복잡해짐
- 기본적으로 메서드 호출 여부를 검증하는 수단이므로 테스트 대상과 모의 객체 간의 상호작용이 조금만 바뀌어도 테스트가 깨짐
☞ 따라서, DAO나 리포지토리와 같이 저장소에 대한 대역은 모의 객체를 사용하는 것보다 메모리를 이용한 가짜 구현을 사용하는 것이 테스트 코드 관리에 유리함