전체 테스트를 돌렸을 때 모든 테스트 메서드마다, 테스트 클래스마다 컨텍스트를 띄우게 된다면 시간이 굉장히 소모된다.
따라서 스프링 부트는 이미 생성된 컨텍스트를 캐싱해서 사용하는 컨텍스트 캐싱을 제공한다.
컨텍스트가 오염되지 않는다면 기존의 캐싱된 컨텍스트를 활용한다.
핵심은 컨텍스트 캐싱을 통해 테스트 성능 최적화를 위해 컨텍스트를 오염시키지 않고 재사용하는 것이다.
컨텍스트 캐싱을 활용할 경우 테스트 격리에 문제가 생기기 때문에 신경을 써야 한다.
레이어별 테스트마다 한번의 컨텍스트를 띄우기 위해 테스트 환경을 구성한다.
인수 테스트의 경우 다음과 같이 구성하여 LineAcceptanceTest extends AcceptanceTest
로 활용했다.
상속을 활용하여 중복코드를 제거할 수 있는 효과 또한 있다.
@Sql({"/scheme.sql", "/data.sql"})
@Sql(value = "/truncate.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class AcceptanceTest {
@Autowired
protected ObjectMapper objectMapper;
@LocalServerPort
private int port;
@BeforeEach
public void setUp() {
RestAssured.port = port;
}
@DirtiesContext
를 활용하면 컨텍스트 캐싱을 활용할 수 없기 때문에 @Sql
을 통해 테스트 데이터 격리를 한다.
@Sql
을 통해 테스트 격리를 하고자 한다면 spring.sql.init.mode = never
설정을 해주어야 한다.
h2 환경에서 테스트를 진행한다면 다음 sql 구문으로 data.sql의 데이터를 id 변화없이 활용할 수 있다.
TRUNCATE TABLE paths RESTART IDENTITY; //truncate와 함께 auto increment 초기화
TRUNCATE TABLE line RESTART IDENTITY;
TRUNCATE TABLE station RESTART IDENTITY;
MockBean을 사용하는 WebMvcTest를 활용한 컨트롤러 단위테스트에선 이런 상속을 활용하는 방법이 유용하다.
매 테스트 클래스마다 MockBean을 새로 등록하게 된다면 컨텍스트를 다시 띄우게 되기 때문이다.
@WebMvcTest({LineController.class, PathController.class, FeeController.class})
public class ControllerTest {
@Autowired
protected MockMvc mockMvc;
@Autowired
protected ObjectMapper objectMapper;
@MockBean
protected LineService lineService;
@MockBean
protected PathService pathService;
@MockBean
protected FeeService feeService;
}
이와 같이 한 클래스에서 불러올 클래스를 모두 선언하고 필요한 MockBean을 모두 선언하면 한 번의 컨텍스트를 띄우는 것으로 캐싱을 통해 사용할 수 있다.
class FeeControllerTest extends ControllerTest {
@Test
...
WebMvcTest에서의 활용과 비슷하다.
@Sql({"/scheme.sql", "/data.sql"})
@Sql(value = "/truncate.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
@JdbcTest
public class DaoTest {
@Autowired
protected JdbcTemplate jdbcTemplate;
protected LineDao lineDao;
protected StationDao stationDao;
protected PathDao pathDao;
protected SubwayRepository subwayRepository;
@BeforeEach
void setUp() {
this.lineDao = new LineDao(jdbcTemplate);
this.stationDao = new StationDao(jdbcTemplate);
this.pathDao = new PathDao(jdbcTemplate);
this.subwayRepository = new SubwayRepository(lineDao, stationDao, pathDao);
}
}
위와 같이 테스트 클래스에서 dao를 활용할 수 있게 protected로 선언해주고 setUp을 통해 모두 생성한다.