Spring Boot

MongoDB 테스트하기(TestContainer 및 memory DB)

수수한개발자 2024. 1. 8.
728x90

 

 

이번에 몽고 디비를 공부하면서 어떻게 테스트할까라는 생각으로 공부하고 글을 작성하게 되었습니다.

기존에는 RDB(MySQL, PostgreSQL)를 사용하면서 hibernate를 사용했으므로 프로덕트 코드는 RDB에 맞게 사용하면서 

테스트 코드는 h2(인메모리 디비)를 사용해 hibernate 설정만 바꿔서 편하게 테스트할 수 있었습니다.

스프링

hibernate를 사용하지 않고 몽고 디비는 nosql이므로 기존의 방식과는 다른 방법으로 테스트를 해야 했다,,

공식적인 Spring 

 

그래서 찾은 방법은 TestContainer와 오픈소스인 memory db를 사용하는 방법이다.

 

 

1. de.flapdoodle.embed.mongo

https://mvnrepository.com/artifact/de.flapdoodle.embed/de.flapdoodle.embed.mongo

 

 

2. mongo-java-server

https://mvnrepository.com/artifact/de.bwaldvogel/mongo-java-server

 

 

3. testcontainer

https://mvnrepository.com/artifact/org.testcontainers/mongodb

 

 

대표적으로 위의 3개가 있다.

보통 3번은 사용하라고 권장되고 있지만 도커가 없는 환경에서는 사용할 수 없기 때문에 저는 이번에 2번과 3번을 사용해 테스트하는 방법을 알아보겠습니다.

 

 

mongo-java-server

 

build.gradle

// mongo-java-server
testImplementation 'de.bwaldvogel:mongo-java-server:1.44.0'

 

추가해 줍니다.

 

 

TestMongoDbConfiguration

import de.bwaldvogel.mongo.MongoServer;
import de.bwaldvogel.mongo.backend.memory.MemoryBackend;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.data.mongodb.MongoDatabaseFactory;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.SimpleMongoClientDatabaseFactory;

@TestConfiguration(proxyBeanMethods = false)
public class TestMongoDBConfiguration {
    
    @Bean
    public MongoTemplate mongoTemplate(MongoDatabaseFactory mongoDbFactory) {
        return new MongoTemplate(mongoDbFactory);
    }

    @Bean
    public MongoDatabaseFactory mongoDbFactory(MongoServer mongoServer) {
        String connectionString = mongoServer.getConnectionString();
        return new SimpleMongoClientDatabaseFactory(connectionString + "/test");
    }

    @Bean
    public MongoServer mongoServer() {
        MongoServer mongoServer = new MongoServer(new MemoryBackend());
        mongoServer.bind();
        return mongoServer;
    }
}

 

 

테스트에서 사용할 수 있는 몽고디비 속성을 빈으로 등록해 줍니다.

 

AcceptanceTest

import org.junit.jupiter.api.BeforeEach;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.ActiveProfiles;

@ActiveProfiles("test")
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@Import(TestMongoDBConfiguration.class)
public abstract class AcceptanceTest {

    @Autowired
    private DatabaseCleanup databaseCleanup;
    
    @BeforeEach
    public void setUp() {
        databaseCleanup.execute();
    }
}

 

위의 등록된 빈을 사용하고 몽고디비를 재사용할 수 있도록 작성한 코드입니다. 위의 클래스를 상속받아 테스트를 작성하면 됩니다.

 

 

 

 

testcontainer

build.gradle

// testcontainers
testImplementation 'org.springframework.boot:spring-boot-testcontainers'
testImplementation 'org.testcontainers:junit-jupiter'
testImplementation 'org.testcontainers:mongodb'

 

 

TestMongoDbConfiguration

기존에는 다음과 같이 컨테이너 정보를 작성했어야 했습니다.

@SpringBootTest
public class TestAppIntegration {

  @Container
  PostgreSQLContainer postgres = new PostgreSQLContainer(DockerImageName.parse("postgres:15.1"));

  @DynamicPropertySource
  static void datasourceProperties(DynamicPropertyRegistry registry) {

    registry.add("spring.datasource.url", postgres::getJdbcUrl);
    registry.add("spring.datasource.username", postgres::getUsername);
    registry.add("spring.datasource.password", postgres::getPassword);
  }

  // tests...
}

 

 

하지만 Spring boot 3.1 이상부터는 @ServiceConnection이란 어노테이션을 지원하면서 다음과 같이 작성할 수 있게 되었습니다.

import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
import org.testcontainers.containers.MongoDBContainer;
import org.testcontainers.utility.DockerImageName;

@TestConfiguration(proxyBeanMethods = false)
public class TestMongoDBConfiguration {

    @Bean(initMethod = "start", destroyMethod = "stop")
    @ServiceConnection
    public MongoDBContainer mongoDBContainer() {
        return new MongoDBContainer(DockerImageName.parse("mongo:latest"));
    }
}

 

위 어노테이션을 사용하게 되면 도커로 띄운 컨테이너와 통신하도록 설정됩니다.

 

@TestContainers와 @Container 어노테이션을 사용하여 빈으로 등록하는 방법 이외에 정적 필드를 통해 정의하는 방법이 있다.

하지만 라이플 사이클을 직접 제어해줘야 하고 docker run이 매번 일어나기 때문에 빈으로 등록하여 라이프 사이클을 스프링과 동일하게 가져가 문제를 해결하였다.

 

장점이자 단점은 도커엔진이 실행되고 있는 상황에서만 사용할 수 있다.

728x90

댓글