인터넷의 많은 예제들을 살펴보면, 각각 테스트를 다 다르게 작성하는 것을 볼 수 있다. 이 글에서 어느 계층에서 어떤 테스트를 선택하고 작성해야 효율적인건지 알아본다.
@SpringBootTest
class TeamServiceTest {
@Autowired
private TeamService teamService;
@Test
@Transactional(propagation = Propagation.NOT_SUPPORTED)
void test() {
Long teamId = teamService.registerTeam("Ateam");
Assertions.assertThat(teamId).isEqualTo(1L);
}
}
| 실행 시간 | |
|---|---|
| 첫번째 | 1sec 279ms |
| 두번째 | 955ms |
| 세번째 | 851ms |
| 네번째 | 1sec 8ms |
| 다섯번째 | 984ms |
| 평균 | 1015.4ms |
@SpringBootTest와 똑같은 방식으로 측정한다.
@ExtendWith(MockitoExtension.class)
class TeamService2Test {
@InjectMocks
private TeamService teamService;
@Mock
private TeamRepository teamRepository;
@Test
void test() {
Team team = new Team(1L,"Ateam", "hyungjunn", 0);
BDDMockito.given(teamRepository.save(any(Team.class)))
.willReturn(team);
Long teamId = teamService.registerTeam("Ateam");
Assertions.assertThat(teamId).isEqualTo(1L);
}
}
각각 측정한 결과를 표로 나타내면 다음과 같다.
| @SpringBootTest | @ExtendWith | |
|---|---|---|
| 첫번째 | 1sec 279ms | 1sec 797ms |
| 두번째 | 955ms | 2sec 130ms |
| 세번째 | 851ms | 1sec 760ms |
| 네번째 | 1sec 8ms | 1sec 795ms |
| 다섯번째 | 984ms | 1sec 803ms |
| 평균 | 1015.4ms | 1857.0ms |
나의 가설은 당연히 서버를 실행시켜야 되는@SpringBootTest가 @ExtendWith(MockitoExtension.class) 보다 느리다는 것이었다.
하지만, 위의 측정 결과는 잘못되었다.
성능 측정을 할때에는 JVM워밍업, I/O 작업 등 외부요인의 영향을 최대한 받지 않아야 하는데, ms단위의 작업은 외부요인이 더 영향을 주는 단계이기 때문이다.
그렇다면 여러번 테스트를 실행시켰을 때는 어떨까? 코드를 재구성해서 측정을 해본다.
@SpringBootTest
class TeamServiceTest {
@Autowired
private TeamService teamService;
private static final int REPEAT_COUNT = 1_000; // 반복 횟수 설정
private static long totalExecutionTime = 0;
@Order(1)
@RepeatedTest(REPEAT_COUNT)
@Transactional(propagation = Propagation.NOT_SUPPORTED)
void test() {
long startTime = System.nanoTime();
Long teamId = teamService.registerTeam("Ateam");
long endTime = System.nanoTime();
long executionTime = endTime - startTime;
totalExecutionTime += executionTime;
Assertions.assertThat(teamId).isNotNull().isGreaterThan(0);
}
@Order(2)
@Test
void calculateAverageExecutionTime() {
long averageExecutionTime = totalExecutionTime / REPEAT_COUNT;
System.out.println("SpringBootTest - Average execution time (ns): "
+ averageExecutionTime);
}
}
@ExtendWith(MockitoExtension.class)
class TeamService2Test {
@InjectMocks
private TeamService teamService;
@Mock
private TeamRepository teamRepository;
private static final int REPEAT_COUNT = 1_000; // 반복 횟수 설정
private static long totalExecutionTime = 0;
@Order(1)
@RepeatedTest(REPEAT_COUNT)
void test() {
Team team = new Team(1L, "Ateam", "hyungjunn", 0);
BDDMockito.given(teamRepository.save(any(Team.class)))
.willReturn(team);
long startTime = System.nanoTime();
Long teamId = teamService.registerTeam("Ateam");
long endTime = System.nanoTime();
long executionTime = endTime - startTime;
totalExecutionTime += executionTime;
Assertions.assertThat(teamId).isNotNull().isGreaterThan(0);
}
@Order(2)
@Test
void calculateAverageExecutionTime() {
long averageExecutionTime = totalExecutionTime / REPEAT_COUNT;
System.out.println("MockitoExtension - Average execution time (ns): "
+ averageExecutionTime);
}
}
여러 번 테스트를 실행시켰을 때의 결과는 아래와 같다.
| 반복 횟수 | @SpringBootTest | @ExtendWith |
|---|---|---|
| 50번 | 1sec 549ms | 2sec 43ms |
| 1000번 | 6sec 543ms | 3sec 796ms |
| 5000번 | 16sec 385ms | 6sec 287ms |
50번일 때는 별로 차이가 안나는 거 같으나, 더 많은 데이터가 실행되면 될 수록 차이가 훨씬 많이 벌어지는 것을 관찰할 수 있었다.