이번 포스트에서는 given-when-then 패턴을 이용한 간단한 단위 테스트를 실행해 본다.
given-when-then 패턴
흔히 사용되는 단위테스트 패턴으로 행동 주도 개발(Behavior Driven Development: BDD)이 있는데 여기서는 테스트 코드 작성 단계를 given - when - then 패턴으로 작성하기를 권장한다.
given: 테스트를 위해 필요한 상황 준비(이런 상황, 객체, 데이터가 주어졌을 때)
when: 테스트하는 메서드 실행(어떤 메서드를 실행했다면)
then: 테스트 결과 검증(어떠한 결과가 나와야 한다.)
JUnit은 애초에 BDD를 위해 만들어진 녀석은 아니기 때문에 이 패턴이 강제되는 내용은 아니다. 따라서 주석으로 "이 부분은 어떤 내용이다." 라고 표시해주면 된다.
일반 상황의 검증
b가 Integer 범위의 자연수일 경우 단위테스트는 간단히 다음과 같이 해볼 수 있다.
@Test
@DisplayName("정상: b가 0이 아닌 경우는 정상적인 값이 반환된다.")
public void seccessOld() {
Calculator calc = new Calculator();
assertEquals(calc.divide(10, 5), 10/5);
}
하지만 이 경우는 코드가 복잡해진다면 어떤 상황에서 테스트가 진행된 것인지 나중에 코드를 보면 또다시 분석이 필요해진다. 남이 짠 코드라면 더더욱 힘들어진다.
이번에는 given-when-then 패턴을 이용해 보자. given-when-then은 특별히 어떤 영역이 지정된 것은 아니고 주석을 통해서 각각 필요한 일을 나타내고 관련 작업을 진행해 주면 된다.
@Test
@DisplayName("정상: b가 0이 아닌 경우는 정상적인 값이 반환된다.")
public void seccess() {
// given
Calculator calc = new Calculator();
int a = 10;
int b = 5;
// when
int result = calc.divide(a, b);
// then
assertEquals(result, 10/5);
}
코드는 훨씬 길어졌지만 테스트 과정에서 무엇을 준비하고 동작시키고 검증하는지가 명확히 표현된다.
예외 상황의 검증
다음은 b가 0이어서 예외가 발생하는 상황에 대한 테스트 예이다.
@Test
@DisplayName("예외: b가 0이므로 ArithmeticException이 발생한다.")
public void error() {
// given
Calculator calc = new Calculator();
int a = 10;
int b = 0;
// when
ArithmeticException e = assertThrows(ArithmeticException.class, ()-> calc.divide(a, b));
// then
assertEquals(e.getMessage(), "/ by zero");
}
F.I.R.S.T
given-when-then과 함께 단위테스트에서 자주 언급되는 원칙 중 하나가 F.I.R.S.T이다.
Fast: 단위테스트는 빠르게 동작해야 한다. 목적을 단순히 설정하고 외부 환경과 무관하게 작성하는 것이 좋다. @SpringBootTest는 편하기는 하지만 상당히 무거운 테스트임으로 지양하고 @DataJpaTest 등 슬라이스 테스트를 하는 것이 좋다.
Independent: 하나의 테스트는 이전 테스트에 의한 상태에 의존해서는 안된다. 따라서 어떤 순서로 테스트를 하더라도 언제나 테스트는 성공해야 한다. 만약 insert 테스트 후 select 테스트가 진행되어야 한다면 안된다. select를 테스트 하기 위해서는 setup에서 insert하고 select 테스트 후 다시 delete 처리 해서 다른 테스트에 영향을 주지 않도록 해야 한다.
Repeatable: 여러 번 반복해서 테스트를 진행하더라도 동일하게 동작해야 한다. 특히 DB와 연관된 테스트를 진행해야 한다면 rollback 시키는 과정이 반드시 필요하다.
Self-Validating: 테스트 자체만으로 검증이 완료되어야 한다. 작업 결과를 개발자가 직접 확인한다면 테스트 자동화가 아니다. 이를 위해 Assert 문장을 작성해야 한다.
Timely: TDD를 한다면 테스트는 production code 개발 전에 진행해야 한다.(모두가 TDD는 아니니까..)