<자바와 JUnit을 활용한 실용주의 단위 테스트> 책과 Junit 5, AssertJ 공식문서를 참고하였습니다.
FIRST 원리
import iloveyouboss.controller.*;
import java.util.*;
import java.util.concurrent.atomic.*;
public class StatCompiler {
private QuestionController controller = new QuestionController();
public Map<String, Map<Boolean, AtomicInteger>> responsesByQuestion(
List<BooleanAnswer> answers) {
Map<Integer, Map<Boolean, AtomicInteger>> responses = new HashMap<>();
answers.stream().forEach(answer -> incrementHistogram(responses, answer));
return convertHistogramIdsToText(responses);
}
private Map<String, Map<Boolean, AtomicInteger>> **convertHistogramIdsToText**(
Map<Integer, Map<Boolean, AtomicInteger>> responses) {
Map<String, Map<Boolean, AtomicInteger>> textResponses = new HashMap<>();
responses.keySet().stream().forEach(id ->
textResponses.put(**controller.find(id)**.getText(), responses.get(id)));
return textResponses;
}
}
public Question find(Integer id) {
return em().find(Question.class, id);
}
convertHistogramIdsToText() 의 역할
id에 맞는 질문 찾기 controller.find(id)textResponses.put(질문, 답변 히스토그램)영속적 저장소(em, 데이터베이스)과 상호 작용(find())하고 있다.
→ convertHistogramIdsToText() 테스트는 느리다.
import iloveyouboss.controller.*;
import java.util.*;
import java.util.concurrent.atomic.*;
public class StatCompiler {
private QuestionController controller = new QuestionController();
public Map<Integer,String> **questionText**(List<BooleanAnswer> answers) {
Map<Integer,String> questions = new HashMap<>();
answers.stream().forEach(answer -> {
if (!questions.containsKey(answer.getQuestionId()))
questions.put(answer.getQuestionId(),
**controller.find(answer.getQuestionId()).getText());**
});
return questions;
}
public Map<String, Map<Boolean, AtomicInteger>> responsesByQuestion(
List<BooleanAnswer> answers, Map<Integer,String> questions) {
Map<Integer, Map<Boolean, AtomicInteger>> responses = new HashMap<>();
answers.stream().forEach(answer -> incrementHistogram(responses, answer));
return convertHistogramIdsToText(responses, **questions**);
}
private Map<String, Map<Boolean, AtomicInteger>> convertHistogramIdsToText(
Map<Integer, Map<Boolean, AtomicInteger>> responses,
**Map<Integer,String> questions**) {
Map<String, Map<Boolean, AtomicInteger>> textResponses = new HashMap<>();
responses.keySet().stream().forEach(id ->
textResponses.put(**questions.get(id)**, responses.get(id)));
return textResponses;
}
}
1 번 역할 ‘컨트롤러에서 id에 맞는 질문 찾기’ 를 questionText()로 분리했다. 이제 convertHistogramIdsToText()는 메모리상의 해시 맵만 사용하며 느린 영속적 저장소를 조회하지 않는다.

좋은 단위 테스트는 다른 단위 테스트에 의존하지 않는다. 각 테스트가 작은 양의 동작에만 집중하면 테스트 코드를 집중적이고 독립적으로 유지하기 쉬워진다.
그래야 테스트가 실패하는 원인을 파악하기 쉽다! 즉, SRP(단일 책임 원칙)에 따라 클래스는 작고 단일한 목적을 가져야 한다.
반복 가능한 테스트는 실행할 때마다 결과가 같아야 한다. 즉, 상황에 따라 성공 여부가 변경되는 테스트 코드를 만들지 말아야 한다. 반복 가능한 테스트를 만들기 위해서는 직접 통제할 수 없는 외부 환경 요소들과 격리시켜야 한다. 하지만 시스템은 불가피하게 통제할 수 없는 요소와 상호 작용해야 할 것이다. 이 경우 테스트 대상 코드와 외부 환경 요소의 독립성을 유지하는 방법으로 Mock 객체를 사용할 수 있다. ex) 현재 시간