코드는 꼭 테스트하라. 아니면 사용자가 하게 된다.
- 데이브 토마스, 앤드류 헌트
6-1. Given-When-Then으로 테스트 구조화
@Test
void setPlanetarySpeedIs7667() {
CruiseControl cruiseControl = new CruiseControl();
cruiseControl.setPreset(SpeedPreset.PLANETARY_SPEED);
Assertions.assertTrue(7667 == cruiseControl.getTargetSpeedKmh());
}
주석을 추가하고, 띄워쓰기를 넣어서 Given, When, Then을 확실히 인지할 수 있다.
void otherTest() {
// given
CruiseControl cruiseControl = new CruiseControl();
// when
cruiseControl.setPreset(SpeedPreset.PLANETARY_SPEED);
// then
Assertions.assertTrue(7667 == cruiseControl.getTargetSpeedKmh());
}
6-2. 의미있는 Assertion 사용하기
Assertions.assertTrue(7667 == cruiseControl.getTargetSpeedKmh());
assertTrue() 는 false인지만 알려주지만, assertEquals()는 기대한 값과, 실제 나온 값을 로그에서 확인가능하다.
Assertions.assertEquals(7667, cruiseControl.getTargetSpeedKmh());
6-3. 실제 값보다 기대 값을 먼저 보이기
assertEquals호출시 인수의 순서에 주의해야 한다. 오해의 소지가 없도록 기대값과 실제값이 로그에 찍히게 하라.
@Test
void setPlanetarySpeedIs7667() {
CruiseControl cruiseControl = new CruiseControl();
cruiseControl.setPreset(SpeedPreset.PLANETARY_SPEED);
Assertions.assertEquals(7667, cruiseControl.getTargetSpeedKmh());
}
6-4. 합당한 허용값 사용하기
자바에서 float나 double은 부동소수점 표현이므로, 0.1로 표현해도 실제 정확히 0.1이 아니다. 이를 명심하고 equal비교시 소수점 넷째 자리등 허용값을 지정해야 한다.
static final double TOLERANCE = 0.00001;
@Test
void testNewTankIsEmpty() {
OxygenTank tank = OxygenTank.withCapacity(100);
Assertions.assertEquals(0, tank.getStatus(), TOLERANCE);
}
@Test
void testFilling() {
OxygenTank tank = OxygenTank.withCapacity(100);
tank.fill(5.8);
tank.fill(5.6);
Assertions.assertEquals(0.114, tank.getStatus(), TOLERANCE);
}
6-5 예외 처리는 JUnit에 맡기기
테스트시에 Exception이 발생해야 한다면 assertThrows()를 사용하라. Exception 이 발생하면 안된다면 메소드 시그니처뒤 에 throws Exception을 넣어서 전체 스택을 뿌려주도록 하라.
Assertions.assertThrows(IOException.class, when);
6-6. 테스트 설명하기
테스트 실패시 이름만 보고도 내용을 알 수 있도록 메서드 명을 상세화하거나 @DisplayName을 이용해서 테스트 내용을 설명하라. 테스트를 비활성화 할때에도 단순히 @Disabled 만 놓지말고, 왜 비활성화 하는지 등 내용을 설명하라.
@Test
@DisplayName("Expect 44% after filling 22l in an empty 50l tank")
@Disabled("We don't have small tanks anymore! TODO: Adapt for big tanks")
6-7. 독립형 테스트 사용하기
@BeforeEach 를 이용해서 자주 사용되는 부분을 넣어놓으면, 테스트 메서드에서 항상 해당 부분을 먼저 보고 와야 한다. 항상 수행하는게 있으면 별도의 메서드로 뽑아내고, 각각의 @Test 부분에서 호출하는 방식을 쓰자.
@BeforeEach
void setUp() {
tank = OxygenTank.withCapacity(10_000);
tank.fill(5_000);
}
@Test
void depressurizingEmptiesTank() {
tank.depressurize();
Assertions.assertTrue(tank.isEmpty());
}
@Test
void completelyFillTankMustBeFull() {
tank.fillUp();
Assertions.assertTrue(tank.isFull());
}
@BeforeEach부분을 createhalfFilledTank()로 빼 냈다.
static OxygenTank createHalfFilledTank() {
OxygenTank tank = OxygenTank.withCapacity(10_000);
tank.fill(5_000);
return tank;
}
@Test
void depressurizingEmptiesTank() {
OxygenTank tank = createHalfFilledTank();
tank.depressurize();
Assertions.assertTrue(tank.isEmpty());
}
@Test
void completelyFillTankMustBeFull() {
OxygenTank tank = createHalfFilledTank();
tank.fillUp();
Assertions.assertTrue(tank.isFull());
}
6-8. 테스트 매개변수화
@Test
void testConversionRoundTrip() {
assertRoundTrip(1);
assertRoundTrip(1_000);
assertRoundTrip(9_999_999);
}
private void assertRoundTrip(int kilometers) {
Distance expectedDistance = new Distance(
DistanceUnit.KILOMETERS,
kilometers
);
Distance actualDistance = expectedDistance
.convertTo(DistanceUnit.MILES)
.convertTo(DistanceUnit.KILOMETERS);
Assertions.assertEquals(expectedDistance, actualDistance);
}
@ParameterizedTest 와 @ValueSource를 활용해서 여러 파라미터로 반복 수행해야 할 경우
@ParameterizedTest(name = "#{index}: {0}km == {0}km->mi->km")
@ValueSource(ints = {1, 1_000, 9_999_999})
void testConversionRoundTrip(int kilometers) {
Distance expectedDistance = new Distance(
DistanceUnit.KILOMETERS,
kilometers
);
Distance actualDistance = expectedDistance
.convertTo(DistanceUnit.MILES)
.convertTo(DistanceUnit.KILOMETERS);
Assertions.assertEquals(expectedDistance, actualDistance);
}
6-9. 경계 케이스 다루기
@Test
void testValidTransmission() {
TransmissionParser parser = new TransmissionParser();
Transmission transmission = parser.parse("032Houston, UFO sighted!");
Assertions.assertEquals(32, transmission.getId());
Assertions.assertEquals("Houston, UFO sighted!",
transmission.getContent());
}
위와 같은 테스트의 경우 null값, 특수문자가 포함된 String 등 경계 케이스를 테스트를 다루어야 한다. int값이면 음수, Integer.MAX_VALUE, Integer.MIN_VALUE 등의 경계값이 있다.
@Test
void testValidTransmission() {
TransmissionParser parser = new TransmissionParser();
Transmission transmission = parser.parse("032Houston, UFO sighted!");
Assertions.assertEquals(32, transmission.getId());
Assertions.assertEquals("Houston, UFO sighted!",
transmission.getContent());
}
@Test
void nullShouldThrowIllegalArgumentException() {
Executable when = () -> new TransmissionParser().parse(null);
Assertions.assertThrows(IllegalArgumentException.class, when);
}
@Test
void malformedTransmissionShouldThrowIllegalArgumentException() {
Executable when = () -> new TransmissionParser().parse("t͡ɬɪŋɑn");
Assertions.assertThrows(IllegalArgumentException.class, when);
}