Diluted Dev
Diluted Dev

Reputation: 115

Populate MongoDb TestContainers in a SpringBoot for integration test

My question is similar to Populate a database with TestContainers in a SpringBoot integration test but instead I have a mongo db test container as follows:

@Container
private static MongoDBContainer mongoDBContainer = new MongoDBContainer(DockerImageName.parse("mongo:3.4.20")) 

I can use mongorepository.save() but that's not really feasible as there are multiple collections and I need populate several fields (and a bunch of them are nested fields). What are some other ways to achieve the same?

Upvotes: 3

Views: 5683

Answers (4)

Garion S.
Garion S.

Reputation: 129

Based on Testcontainers documentation and one of the answers here, I have added import in singleton container. My initial test data is stored in JSON format, so I used mongoimport.

public class AbstractContainerBaseTest {

    protected static final MongoDBContainer mongoDBContainer;

    static {
        mongoDBContainer = new MongoDBContainer(DockerImageName.parse("mongo:latest"));
        mongoDBContainer.start();
        mongoDBContainer.copyFileToContainer(MountableFile.forClasspathResource("/mongo/article.json"), "/article.json");
        try {
            mongoDBContainer.execInContainer("mongoimport", "-d", "test", "-c", "article", "--file", "/article.json", "--jsonArray");
        } catch (Exception e) {
            // logger
        }
    }

    @DynamicPropertySource
    static void mongoDbProperties(DynamicPropertyRegistry registry) {
        registry.add("spring.data.mongodb.uri", mongoDBContainer::getReplicaSetUrl);
    }
}

Test classes extends then this abstract class:

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@Testcontainers
class ArticleControllerTest extends AbstractContainerBaseTest {
}

Upvotes: 1

kristof.taveirne
kristof.taveirne

Reputation: 175

A way I've done it in the past when I need some initial data in the database is by adding an ApplicationContextInitializer that boots up the testcontainer and then run a mongorestore inside the container for loading a mongodump I prepared separately.

This way you can keep your dump folder in your test-resources folder. Ofcourse, if you have other files there be sure to use to the correct classpath resource path.

Hope this helps!

public class TestContainerInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
    
    @SneakyThrows
    @Override
    public void initialize(ConfigurableApplicationContext context) {
        MongoDBContainer instance = MongoContainerSingleton.getInstance();
        instance.copyFileToContainer(
                    MountableFile.forClasspathResource("/"), "/IT-dump");
        Container.ExecResult mongorestore = instance.execInContainer("mongorestore", "/IT-dump/dump/");
    }
    
    public static class MongoContainerSingleton {
    
        private static volatile MongoDBContainer instance = null;
    
        public static MongoDBContainer getInstance() {
            synchronized (MongoContainerSingleton.class) {
                if (instance == null) {
                    instance = new MongoDBContainer("mongo:4.2.11")
                            .withReuse(true);
                    instance.start();
                }
            }
            return instance;
        }
    }
}

Upvotes: 6

Edd&#250; Mel&#233;ndez
Edd&#250; Mel&#233;ndez

Reputation: 6530

There is a liquibase mongodb project which can be used. You can take a look at this project. There is a db.changelog-master.json where the schema creation is defined as a first changelog (you can define more) and as you can see in the test just defined the container, set the spring.data.mongodb.uri and manually run the migration due to spring boot does not offer autoconfigure for liquibase mongodb extension.

 @Container
    private static final MongoDBContainer mongo = new MongoDBContainer("mongo:4.0.10");

    @Autowired
    private PersonRepository repository;

    @DynamicPropertySource
    static void mongoProperties(DynamicPropertyRegistry registry) {
        registry.add("spring.data.mongodb.uri", mongo::getConnectionString);
    }

    @Test
    void test() throws LiquibaseException {
        var database = (MongoLiquibaseDatabase) DatabaseFactory.getInstance().openDatabase(mongo.getReplicaSetUrl("test"), null, null, null, null);
        var liquibase = new Liquibase("db/changelog/db.changelog-master.json", new ClassLoaderResourceAccessor(), database);
        liquibase.update("");

        var books = this.repository.findAll();
        assertThat(books).hasSize(3);
    }

This sample project is based in spring boot too.

Also, check Initializing a fresh instance

Upvotes: 2

Kevin Wittek
Kevin Wittek

Reputation: 1572

I am not a MongoDB expert, but you should populate the database independent of the fact, that you are using Testcontainers to instrument it. So using the repository does seem right. You can also use a special repository in your test classes, to which you add methods that do bigger bootstrappings.

Also, consider stopping using the @Container annotation, which starts a container for every test class, this can lead to a lot of startup overhead. Generally, using the Singleton Container Pattern lead to much better test suite performance.

Upvotes: 0

Related Questions