PostgreSQL integration tests using Testcontainers

PostgreSQL integration tests using Testcontainers

Running tests against a real database provides more confidence in comparison to an in-memory database such as H2. For that, the Testcontainers library is a wise choice as it provides an out-of-the-box solution to write and run integration tests against an actual database of your choice running in a Docker container. In this tutorial, we focus on how to write PostgreSQL integration tests using Testcontainers for a Spring Boot service.

Add Testcontainers and PostgreSQL dependencies

Before adding the dependencies, you must ensure your code functions correctly and can connect to PostgreSQL successfully. You can refer to this sample project we prepared to learn that.

After that, add the following dependencies to your project.

<dependency>
    <groupId>org.testcontainers</groupId>
    <artifactId>testcontainers</artifactId>
    <version>1.18.3</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.testcontainers</groupId>
    <artifactId>junit-jupiter</artifactId>
    <version>1.18.3</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.testcontainers</groupId>
    <artifactId>postgresql</artifactId>
    <version>1.18.3</version>
    <scope>test</scope>
</dependency>

Write integration tests

We can start writing some integration tests write after adding the dependencies. For that, we need to annotate the test class with the @Testcontainers annotation and bootstrap the PostgreSQL Docker container as follows,

import com.madadipouya.example.multiple.datasource.lyrics.repository.LyricsRepository
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.test.context.DynamicPropertyRegistry
import org.springframework.test.context.DynamicPropertySource
import org.springframework.test.context.junit.jupiter.SpringExtension
import org.testcontainers.containers.PostgreSQLContainer
import org.testcontainers.junit.jupiter.Container
import org.testcontainers.junit.jupiter.Testcontainers
import org.testcontainers.utility.DockerImageName

@Testcontainers
@ExtendWith(SpringExtension::class)
@SpringBootTest
internal class LyricsServiceTest {

    companion object {
        @Container
        private val postgresqlContainer = PostgreSQLContainer(DockerImageName.parse("postgres:15.3"))

        @JvmStatic
        @DynamicPropertySource
        fun dataSourcesProperties(registry: DynamicPropertyRegistry) {
            registry.add("spring.datasource.jdbcUrl") { postgresqlContainer.jdbcUrl }
            registry.add("spring.datasource.username") { postgresqlContainer.username }
            registry.add("spring.datasource.password") { postgresqlContainer.password }
        }
    }

    @Autowired
    lateinit var lyricsRepository: LyricsRepository
    
    @AfterEach
    fun cleanDatabase() {
        songRepository.deleteAll()
        lyricsRepository.deleteAll()
    }

    @Test
    fun `should insert a song with lyrics to two datasources`() {
        val result = lyricsRepository.save(Lyrics(getTwoForTragedyLyrics())).id!!
        assertTrue(lyricsRepository.existsById(result))
    }

    fun getTwoForTragedyLyrics() = """
        Sleep Eden sleep
        My fallen son
        Slumber in peace...
        """
}

In the above code, first, we start a PostgreSQL Docker container and then use the Spring Boot @DynamicPropertySource annotation to override database connection properties and finally add a test to insert lyrics to the database table and verify its existence.

Additionally, we clear the database content after each test run using the JUnit @AfterEach annotation.

Lastly, we set the spring.datasource.initialization-mode=always to create database tables if they don’t exist automatically.

Conclusion

In this article, we covered how to write PostgreSQL integration tests using Testcontainers in Spring Boot. The approach is similar to any Testcontainers test. We also utilized Spring Boot’s dynamic property source feature to easily set the Testcontainers database properties.

As always, the fully functional demo project is available on GitHub at the following link:

https://github.com/kasramp/spring-multiple-datasources

Inline/featured images credits