Skip to content

Commit 44dafad

Browse files
feat: Use Testcontainers for controller tests (#51)
[Testcontainers](https://github.com/testcontainers) allow us to spin up a temporary Docker image during tests. For this project, we will spin up a temporary PostgreSQL database to run the controller tests against a real PostgreSQL database instead of an in-memory H2 database. There are differences between PostgreSQL and H2 databases so if you plan to use a PostgreSQL database in production, it also makes sense to mimic this by using a PostgreSQL database in the tests. H2 databases are useful for quickly testing and running the system locally, but it is better to use Testcontainers to run a real PostgreSQL database for the controller tests. Use Testcontainers for controller tests: - Add Gradle dependencies to `local.versions.toml` and `api/build.gradle.kts`: - `org.springframework.boot:spring-boot-testcontainers` - `org.testcontainers:postgresql ` - Add `TestContainerConfig` which defines the PostgreSQL container and database settings. - Add annotations `@Import(TestContainerConfig::class)` and `@ActiveProfiles("postgres")` to `CustomerControllerTest` to ensure that a PostgreSQL database is used for the tests. - Add `application-postgres.yml` with connection settings for PostgreSQL. - Update `README.md`.
1 parent c3d77da commit 44dafad

File tree

6 files changed

+55
-0
lines changed

6 files changed

+55
-0
lines changed

README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,17 @@ to view the Swagger documentation.
3939
- [PostgreSQL](https://www.postgresql.org/) - Open-source relational database
4040
- [H2database](https://github.com/h2database/h2database) - Provides an in-memory database for simple local testing
4141
- [Liquibase](https://github.com/liquibase/liquibase) - Used to manage database schema changelogs
42+
- [Testcontainers](https://github.com/testcontainers) - Creates a temporary PostgreSQL database for tests
43+
44+
## Testing
45+
You can run the tests for this project using the following command:
46+
```
47+
./gradlew test
48+
```
49+
Please note that this project uses
50+
[Testcontainers](https://github.com/testcontainers)
51+
to create a temporary PostgreSQL database for tests. This requires
52+
a local Docker instance to be running when executing the tests.
4253

4354
## Gradle best practices for Kotlin
4455
[kotlinlang.org](https://kotlinlang.org/docs/gradle-best-practices.html)

apps/api/build.gradle.kts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ dependencies {
3131

3232
// Test dependencies
3333
testImplementation(local.springboot.starter.test)
34+
testImplementation(local.springboot.testcontainers)
35+
testImplementation(local.testcontainers.postgresql)
3436
testImplementation(local.kotlin.test.junit5)
3537
testRuntimeOnly(local.junit.platform.launcher)
3638
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
spring:
2+
datasource:
3+
url: jdbc:postgresql://localhost:5432/sample-db
4+
username: postgres
5+
password: postgres
6+
liquibase:
7+
enabled: true

apps/api/src/test/kotlin/com/github/thorlauridsen/CustomerControllerTest.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,17 @@ import org.springframework.test.web.servlet.MockMvc
1515
import java.util.UUID
1616
import org.junit.jupiter.params.ParameterizedTest
1717
import org.junit.jupiter.params.provider.ValueSource
18+
import org.springframework.context.annotation.Import
19+
import org.springframework.test.context.ActiveProfiles
1820

1921
/**
2022
* Test class for testing the CustomerController.
2123
* This class extends the [BaseMockMvc] class so this will spin up a Spring Boot instance for the tests.
24+
* A local Docker instance is required to run the tests as Testcontainers is used.
2225
* @param mockMvc The MockMvc instance to use for testing.
2326
*/
27+
@ActiveProfiles("postgres")
28+
@Import(TestContainerConfig::class)
2429
class CustomerControllerTest(
2530
@Autowired mockMvc: MockMvc,
2631
@Autowired private val objectMapper: ObjectMapper
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package com.github.thorlauridsen
2+
3+
import org.springframework.boot.test.context.TestConfiguration
4+
import org.springframework.boot.testcontainers.service.connection.ServiceConnection
5+
import org.springframework.context.annotation.Bean
6+
import org.testcontainers.containers.PostgreSQLContainer
7+
8+
@TestConfiguration(proxyBeanMethods = false)
9+
class TestContainerConfig {
10+
11+
/**
12+
* A [PostgreSQLContainer] bean to be used in tests.
13+
* This uses Testcontainers to spin up a temporary PostgreSQL instance in a Docker container.
14+
* The [ServiceConnection] annotation allows Spring Boot to automatically
15+
* configure the datasource properties based on the container settings.
16+
*/
17+
@Bean
18+
@ServiceConnection // lets Spring Boot wire spring.datasource.* automatically
19+
fun postgresContainer(): PostgreSQLContainer<*> {
20+
return PostgreSQLContainer("postgres:17")
21+
.withDatabaseName("sample-db")
22+
.withUsername("postgres")
23+
.withPassword("postgres")
24+
}
25+
}

gradle/local.versions.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ postgres = "42.7.7"
88
springboot = "3.5.5"
99
springDependencyPlugin = "1.1.7"
1010
springdoc = "2.8.11"
11+
testcontainers = "1.21.3"
1112

1213
[libraries]
1314
# H2 for an in-memory database
@@ -33,6 +34,10 @@ springboot-starter-jpa = { module = "org.springframework.boot:spring-boot-starte
3334
springboot-starter-test = { module = "org.springframework.boot:spring-boot-starter-test", version.ref = "springboot" }
3435
springboot-starter-validation = { module = "org.springframework.boot:spring-boot-starter-validation", version.ref = "springboot" }
3536
springboot-starter-web = { module = "org.springframework.boot:spring-boot-starter-web", version.ref = "springboot" }
37+
springboot-testcontainers = { module = "org.springframework.boot:spring-boot-testcontainers", version.ref = "springboot" }
38+
39+
# Testcontainers for running PostgreSQL in tests
40+
testcontainers-postgresql = { module = "org.testcontainers:postgresql", version.ref = "testcontainers" }
3641

3742
# Springdoc provides swagger docs with support for Spring Web MVC
3843
springdoc-openapi-starter-webmvc = { module = "org.springdoc:springdoc-openapi-starter-webmvc-ui", version.ref = "springdoc" }

0 commit comments

Comments
 (0)