[Test] Embedded Redis 설정으로 알아본 Spring Integration Test

shinny·2024년 6월 23일

Why Embedded Redis?

  • @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();
    }
}

통합 테스트란 SpringApplicationContext와 유사한 테스트 환경을 만들고, 이 ApplicationContext가 문제 없이 잘 동작하는지를 테스트하는 것이다.

Integration Test의 목적

개요

Spring 공식 문서에서는 Integration Test가 아래의 것들을 테스트한다고 적혀있다.

  • 스프링 IoC 컨테이너의 컨텍스트들이 정확하게 연결되어 있는지 여부
  • JDBC 혹은 ORM 툴을 사용한 데이터 접근성. SQL문, Hibernate query, JPA entity mapppings 등이 제대로 되어있는지 여부

테스트를 할 때, 애플리케이션 서버를 실제 환경에 '배포'하지 않고, 또 외부 인프라와 실제로 '연결'하지 않는 것이 중요하다. 한마디로, SpaceX가 로켓을 실제로 발사하기 전, 실제와 가장 유사한 환경을 만들어서 테스트를 수도 없이 반복하는 것과 유사하다.

Application Server는 외부 인프라(ex. DB)와 연결되어 있다. 그러면 연결 지점이 있는데 그 지점 자체는 동일하게 구현을 해주지만, 실제 DB와는 연결을 하지 않고, DB 같은 것과 연결을 시켜준다. 여기서 말하는 'DB 같은 것'이 바로 여기서는 EmbeddedRedis다.

목적 상세

스프링의 통합 테스트 지원(support)은 아래 네 가지 주요 목표를 갖는다.

  • 테스트 간 스프링 IoC 컨테이너의 캐싱
  • 테스트에 필요한 객체 혹은 리소스의 의존성 주입
  • 트랜잭션 관리
  • 스프링만의 테스트 base Classes 제공

Spring TestContext Framework

Integration Test를 실행하는 클래스 위에는 @SpringBootTest 어노테이션을 붙인다.

@SpringBootTest란?

어노테이션에 우클릭을 해서 들어갔더니 다음과 같은 설명이 적혀있다.

스프링 부트 기반의 테스트를 실행하는 테스트에 붙이는 어노테이션. Spring TestContext Framework 위에서 다음과 같은 기능을 제공한다.

  • 특정한 @ContextConfiguration(loader=...)가 정의되지 않았을 때, 기본 ContextLoaderSpringBootContextLoader를 사용한다.
  • 명시적인 클래스가 없을 때는 @SpringBootConfiguration을 자동으로 검색한다.
  • properties를 사용해서, 환경 변수를 설정하는 것이 가능하다.
  • args를 사용해서, 어플리케이션 실행 명령 시 변수를 정의하는 것이 가능하다.
  • 다른 webEnvironment modes에 대한 지원을 제공한다.(ex. port number)

Spring TestContext의 ApplicationContext

  • Test 환경에서의 ApplicationContext는 본 코드의 ApplicationContext와는 독립적으로 구성되고, @TestCofiguration, @MockBean을 주입받아서 사용된다.

질문 1. Integration Test에서의 CPU, Memory 사용량이 실제 애플리케이션 코드 실행의 성능에 영향을 미치나?

Embedded Redis는 어디서 실행되는 거지?

EmbeddedRedis 클래스의 코드를 보면 포트 번호가 63790으로 매핑이 되어 있다. 하지만 나는 로컬 환경의 63790 포트에서 Redis를 실행하고 있지 않다. 하지만 Redis에 Query를 실행하는 코드들이 잘 동작한다. 왜 그런걸까?

바로 Java Virtual Machine(JVM)위에서 Redis가 말 그대로 임베디드 즉 내장 형태로 실행되기 때문이다. JVM 위에서 실행되는 Tomcat 내장 서버 옆에서 Redis 서버도 함께 실행이 되고 있다. 그러면 Redis가 어느 정도의 메모리를 사용하는 거지? 테스트 코드 실행 시, Redis가 너무 많은 메모리를 사용하게 되면 실제 애플리케이션 코드에도 악영향을 주는 것이 아닌가?

우선 내가 사용하고 있는 embedded-redis 라이브러리는 몇 MB 정도의 크기였고, 메모리 사용량도 적었다.
그리고 실제 CI/CD 배포 전략에서 통합 테스트 코드가 100% pass되었을 때, 실제 배포가 되어 본 애플리케이션 코드가 실행되도록 한다. 그래서 서로 자원 경쟁이 일어나지 않는 구조로 설계되어있다.

결론

  • 실제 애플리케이션 서버의 성능에 영향을 주지 않는다.
  • 테스트 코드 실행에서 띄워지는 ApplicationContext가 모두 Destroy되고 나서 본 코드의 ApplicationContext가 올라간다면 문제가 없다.

질문 2. 통합 테스트가 여러 개일 경우, ApplicationContext가 반복적으로 올라갔다가 내려가나?

Spring은 테스트 환경에서 ApplicationContext를 효율적으로 관리하기 위해, 컨텍스트 캐싱을 사용한다. 만약에 테스트 클래스 간 설정이 다르다면, 새롭게 컨텍스트를 만들 수 있지만 이런 예외적인 경우가 아니라면, 이미 생성된 ApplicationContext를 캐싱하여 재사용한다.

(공식문서) TestContext 프레임워크가 ApplicationContext(혹은 WebApplicationContext)를 생성하면, 그 context는 캐시되고 동일한 테스트 환경(test suite)에서 동일한 unique 컨텍스트 설정을 사용하는 다음 테스트에서 재사용된다.

(추가) H2 Database로 독립 테스트 환경 구축
개발 환경 DB를 사용한다고 하더라도, 여러 개발자가 테스트를 동시 수행할 수 있고, 또 기타 다양한 이유로 데이터 변경이 일어날 수 있기 때문에 정확한 테스트가 이루어지지 않을 수 있다. Redis와 마찬가지로 RDB의 경우에도 테스트 환경에서만 사용하는 임베디드 Database를 사용함으로써 위와 같은 문제를 방지하고, 테스트 코드에만 집중할 수 있다. (참고자료(링크))

참고자료

  • Context Caching(링크)
  • Spring TestContext Framework(링크)
  • Integration Testing(링크)
profile
꾸준히, 성실하게, 탁월하게 매일 한다

0개의 댓글