Testable Code
1. 무엇을 테스트할 것인가
1-1. 구현부가 아닌 설계를 테스트 해야한다
테스트를 위해 원본의 구현과 설계를 고치는 것이 맞는가?
- 테스트를 위해 구현과 설계는 변경될 수 있다.
- 테스트 코드는 보조 수단이 아닌 같은 레벨로 봐야 한다.
- 좋은 디자인으로 구현된 코드는 테스트하기 쉽다.
- 테스트는 구현 설계 Smell을 맡게 해주는 좋은 수단
1-2. 테스트가 가능한 것, 불가능한 것
- Non-Testable: 제어할 수 없는 영역 ⇒ 멱등한 결과를 보장할 수 없음
Random
, Shuffle
, LocalDateTime.now()
- 외부 세계(HTTP, 외부 저장소)
- Testable: 항상 성공할 수 있는 것, 항상 동일한 결과가 나올 수 있는 것
2. 어떻게 테스트할 것인가
2-1. 테스트가 불가능한 영역을 Boundary Layer로 올려서 테스트


- 배달팁을 계산하는 로직에서 isValid의
LocalDateTime.now()
는 제어할 수 없는 영역이기에 테스트가 어려움

LocalDateTime.now()
를 Boundary Layer로 끌어올려서 테스트가 가능하도록 변경시킴
어디까지가 Boundary Layer인가?
⇒ 한 모듈로서의 의미를 지니는 가장 바깥쪽
2-2. Java, Spring Framework
@SpringBootTest
- 통합 테스트를 지원하는 스프링 부트 테스트 어노테이션
- 모든 빈들을 스캔하고 애플리케이션 Context를 생성하여 테스트 진행
- Spring Context는 느리다 ⇒ 빠른 피드백을 받을 수 없다
- Spring Context의 오용은 언어의 본질을 망각하게 할 수 있다
- Context, Framework에 의존적이지 않은 테스트를 작성하는 것이 중요!
2-3. Test Double
- Test Double이란?
- 테스트 중인 시스템의 일부분이 완전히 준비되지 않았거나 테스트하기 어려운 상황에서 그 대안으로 사용될 수 있는 '가짜' 컴포넌트
- Test Double의 종류
- Dummy: 실제로 사용되진 않지만 파라미터 리스트를 채우기 위한 객체
- Fake: 실제 객체의 간단한 버전. 가벼운 데이터베이스 서버나 간단한 로직을 가진 컴포넌트로 작동
- Stub: 테스트 중에 호출되면 미리 준비된 응답을 제공. 특정 메서들 호출에 의한 반환값을 설정하거나 외부 서비스, 컴포넌트를 대체하는데 사용
- Spy: Stub과 유사하지만 호출되었을 때의 정보 기록. 테스트에서 어떤 메서드가 어떻게, 몇 번 호출되었는지 확인 가능
- Mock: 예상된 호출 명세를 정의하며 테스트에서 이 명세가 충족되지 않으면 테스트 실패
- 대표적인 Test Double에는 Mockito가 있음
- 무엇을 Test Double로 처리해야 할까?
- Test Double의 남용은 구현 테스트로 유도할 수 있음
- Boundary Layer로 끌어 올려진 Non-Testable 코드에 대해 Test Double 처리
2-4. Embedded
2-5. EndPoint Test
- MockMvc, REST Assured, WebTestClient
- 엔드 포인트 테스트는 요청과 응답 스펙 검증만으로 제한하는게 좋음
2-6. Spring Cloud Contract
- MSA 환경에서 E2E 테스트를 위해서 만들어진 stub 기반 test 도구
- CDC(Consumer-Driven Contract)를 잘 이뤄질 수 있도록 Contract 공유 메커니즘을 제공

참고
https://www.youtube.com/watch?v=YdtknE_yPk4
https://jwchung.github.io/testing-oh-my
https://jojoldu.tistory.com/674
https://jojoldu.tistory.com/676?category=1036934
https://jojoldu.tistory.com/680?category=1036934