Skip to content

Commit a4393ea

Browse files
feat: Use Testcontainers for controller tests (#60)
[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 8c13479 commit a4393ea

File tree

6 files changed

+60
-1
lines changed

6 files changed

+60
-1
lines changed

README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,17 @@ to view the Swagger documentation.
3737
- [H2database](https://github.com/h2database/h2database) - Provides an in-memory database for simple local testing
3838
- [Liquibase](https://github.com/liquibase/liquibase) - Used to manage database schema changelogs
3939
- [Lombok](https://github.com/projectlombok/lombok) - Used to reduce boilerplate code
40+
- [Testcontainers](https://github.com/testcontainers) - Creates a temporary PostgreSQL database for tests
41+
42+
## Testing
43+
You can run the tests for this project using the following command:
44+
```
45+
./gradlew test
46+
```
47+
Please note that this project uses
48+
[Testcontainers](https://github.com/testcontainers)
49+
to create a temporary PostgreSQL database for tests. This requires
50+
a local Docker instance to be running when executing the tests.
4051

4152
## Gradle best practices
4253
[kotlinlang.org](https://kotlinlang.org/docs/gradle-best-practices.html)

apps/api/build.gradle.kts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,10 @@ dependencies {
2929
// Springdoc OpenAPI for providing Swagger documentation
3030
implementation(local.springdoc.openapi.starter.webmvc)
3131

32-
// Spring Boot test dependencies
32+
// Spring Boot and Testcontainers test dependencies
3333
testImplementation(local.springboot.starter.test)
34+
testImplementation(local.springboot.testcontainers)
35+
testImplementation(local.testcontainers.postgresql)
3436

3537
// JUnit platform launcher dependency for running JUnit tests
3638
testRuntimeOnly(local.junit.platform.launcher)
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/java/com/github/thorlauridsen/CustomerControllerTest.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,23 @@
1010
import org.junit.jupiter.params.ParameterizedTest;
1111
import org.junit.jupiter.params.provider.ValueSource;
1212
import org.springframework.beans.factory.annotation.Autowired;
13+
import org.springframework.context.annotation.Import;
1314
import org.springframework.http.HttpStatus;
15+
import org.springframework.test.context.ActiveProfiles;
1416
import org.springframework.test.web.servlet.MockMvc;
1517

1618
import static com.github.thorlauridsen.controller.BaseEndpoint.CUSTOMER_BASE_ENDPOINT;
1719
import static org.junit.jupiter.api.Assertions.assertEquals;
1820
import static org.junit.jupiter.api.Assertions.assertNotNull;
1921
import static org.junit.jupiter.api.Assertions.assertTrue;
2022

23+
/**
24+
* Test class for testing the CustomerController.
25+
* This class extends the BaseMockMvc class so this will spin up a Spring Boot instance for the tests.
26+
* A local Docker instance is required to run the tests as Testcontainers is used.
27+
*/
28+
@ActiveProfiles("postgres")
29+
@Import(TestContainerConfig.class)
2130
class CustomerControllerTest extends BaseMockMvc {
2231

2332
@Autowired
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+
public 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
19+
public PostgreSQLContainer<?> postgresContainer() {
20+
return new 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
@@ -31,6 +32,10 @@ springboot-starter-jpa = { module = "org.springframework.boot:spring-boot-starte
3132
springboot-starter-test = { module = "org.springframework.boot:spring-boot-starter-test", version.ref = "springboot" }
3233
springboot-starter-validation = { module = "org.springframework.boot:spring-boot-starter-validation", version.ref = "springboot" }
3334
springboot-starter-web = { module = "org.springframework.boot:spring-boot-starter-web", version.ref = "springboot" }
35+
springboot-testcontainers = { module = "org.springframework.boot:spring-boot-testcontainers", version.ref = "springboot" }
36+
37+
# Testcontainers for running PostgreSQL in tests
38+
testcontainers-postgresql = { module = "org.testcontainers:postgresql", version.ref = "testcontainers" }
3439

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

0 commit comments

Comments
 (0)