@TestConfiguration으로 작성한 EmbeddedRedis 클래스의 코드@TestConfiguration
public class EmbeddedRedis {
private final RedisServer redisServer;
public EmbeddedRedis() throws IOException {
this.redisServer = new RedisServer(63790);
}
@PostConstruct
public void start() throws IOException {
this.redisServer.start();
}
@PreDestroy
public void stop() throws IOException {
this.redisServer.stop();
}
}
통합 테스트란 Spring의 ApplicationContext와 유사한 테스트 환경을 만들고, 이 ApplicationContext가 문제 없이 잘 동작하는지를 테스트하는 것이다.
Spring 공식 문서에서는 Integration Test가 아래의 것들을 테스트한다고 적혀있다.
테스트를 할 때, 애플리케이션 서버를 실제 환경에 '배포'하지 않고, 또 외부 인프라와 실제로 '연결'하지 않는 것이 중요하다. 한마디로, SpaceX가 로켓을 실제로 발사하기 전, 실제와 가장 유사한 환경을 만들어서 테스트를 수도 없이 반복하는 것과 유사하다.
Application Server는 외부 인프라(ex. DB)와 연결되어 있다. 그러면 연결 지점이 있는데 그 지점 자체는 동일하게 구현을 해주지만, 실제 DB와는 연결을 하지 않고, DB 같은 것과 연결을 시켜준다. 여기서 말하는 'DB 같은 것'이 바로 여기서는 EmbeddedRedis다.
스프링의 통합 테스트 지원(support)은 아래 네 가지 주요 목표를 갖는다.
Integration Test를 실행하는 클래스 위에는 @SpringBootTest 어노테이션을 붙인다.
어노테이션에 우클릭을 해서 들어갔더니 다음과 같은 설명이 적혀있다.
스프링 부트 기반의 테스트를 실행하는 테스트에 붙이는 어노테이션. Spring TestContext Framework 위에서 다음과 같은 기능을 제공한다.
@ContextConfiguration(loader=...)가 정의되지 않았을 때, 기본 ContextLoader로 SpringBootContextLoader를 사용한다.@SpringBootConfiguration을 자동으로 검색한다.properties를 사용해서, 환경 변수를 설정하는 것이 가능하다.args를 사용해서, 어플리케이션 실행 명령 시 변수를 정의하는 것이 가능하다.webEnvironment modes에 대한 지원을 제공한다.(ex. port number)ApplicationContext는 본 코드의 ApplicationContext와는 독립적으로 구성되고, @TestCofiguration, @MockBean을 주입받아서 사용된다.위 EmbeddedRedis 클래스의 코드를 보면 포트 번호가 63790으로 매핑이 되어 있다. 하지만 나는 로컬 환경의 63790 포트에서 Redis를 실행하고 있지 않다. 하지만 Redis에 Query를 실행하는 코드들이 잘 동작한다. 왜 그런걸까?
바로 Java Virtual Machine(JVM)위에서 Redis가 말 그대로 임베디드 즉 내장 형태로 실행되기 때문이다. JVM 위에서 실행되는 Tomcat 내장 서버 옆에서 Redis 서버도 함께 실행이 되고 있다. 그러면 Redis가 어느 정도의 메모리를 사용하는 거지? 테스트 코드 실행 시, Redis가 너무 많은 메모리를 사용하게 되면 실제 애플리케이션 코드에도 악영향을 주는 것이 아닌가?
우선 내가 사용하고 있는 embedded-redis 라이브러리는 몇 MB 정도의 크기였고, 메모리 사용량도 적었다.
그리고 실제 CI/CD 배포 전략에서 통합 테스트 코드가 100% pass되었을 때, 실제 배포가 되어 본 애플리케이션 코드가 실행되도록 한다. 그래서 서로 자원 경쟁이 일어나지 않는 구조로 설계되어있다.
Spring은 테스트 환경에서 ApplicationContext를 효율적으로 관리하기 위해, 컨텍스트 캐싱을 사용한다. 만약에 테스트 클래스 간 설정이 다르다면, 새롭게 컨텍스트를 만들 수 있지만 이런 예외적인 경우가 아니라면, 이미 생성된 ApplicationContext를 캐싱하여 재사용한다.
(공식문서) TestContext 프레임워크가 ApplicationContext(혹은 WebApplicationContext)를 생성하면, 그 context는 캐시되고 동일한 테스트 환경(test suite)에서 동일한 unique 컨텍스트 설정을 사용하는 다음 테스트에서 재사용된다.
(추가) H2 Database로 독립 테스트 환경 구축
개발 환경 DB를 사용한다고 하더라도, 여러 개발자가 테스트를 동시 수행할 수 있고, 또 기타 다양한 이유로 데이터 변경이 일어날 수 있기 때문에 정확한 테스트가 이루어지지 않을 수 있다. Redis와 마찬가지로 RDB의 경우에도 테스트 환경에서만 사용하는 임베디드 Database를 사용함으로써 위와 같은 문제를 방지하고, 테스트 코드에만 집중할 수 있다. (참고자료(링크))