Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .github/workflows/gradle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,6 @@ jobs:
- name: Test with Gradle
uses: gradle/gradle-build-action@v2
with:
arguments: test
arguments: test
env:
TESTCONTAINERS_REUSE_ENABLE: true
74 changes: 40 additions & 34 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# Spring-TestContainers

Spring-TestContainers is a Java library that simplifies database integration testing by automating Testcontainers setup and lifecycle management—seamlessly integrated with Spring and Spring Boot.
Spring-TestContainers is a Java library that makes it easier to write integration tests with Testcontainers, especially when you're using Spring or Spring Boot. It handles the setup and lifecycle of containers for you, so you can focus on testing—not boilerplate.

We originally built this for FlowInquiry to make our own testing smoother. It worked so well, we decided to share it as a standalone library so other teams can take advantage of it too.

## Why Spring-TestContainers?

Expand All @@ -12,6 +14,19 @@ Setting up Testcontainers in Spring-based projects often involves boilerplate co

* Auto-configures Spring environment with database connection details

### Supported Containers

Spring-TestContainers provides out-of-the-box support for the following containers. You can enable each one via a dedicated annotation in your test classes:

Spring-TestContainers provides out-of-the-box support for the following containers. You can enable each one via a dedicated annotation in your test classes:

| Container | Annotation | Example Usage | Notes |
|----------------|-----------------------------------|-----------------------------------------------------|--------------------------------|
| **PostgreSQL** | `@EnablePostgresContainer` | `@EnablePostgresContainer(version = "15")` | Uses `PostgreSQLContainer` |
| **MySQL** | `@EnableMySQLContainer` | `@EnableMySQLContainer(version = "8")` | Uses `MySQLContainer` |
| **Ollama (AI)**| `@EnableOllamaContainer` | `@EnableOllamaContainer(model = "llama2")` | Starts Ollama with auto-pull |


## Comparison: TestContainers with Spring vs Spring-TestContainers

The following table demonstrates the difference between using TestContainers with Spring directly and using Spring-TestContainers:
Expand Down Expand Up @@ -103,7 +118,7 @@ class PostgresTest {

## Features

* 🧩 Simple annotation API: @EnablePostgreSQL, @EnableMySQL
* 🧩 Simple annotation API: @EnablePostgreSQL, @EnableMySQL, @EnableOllmaContainer

* 🔄 Automatic container lifecycle management

Expand All @@ -130,30 +145,19 @@ Add the core library along with the database module(s) you plan to use. Each dat
### Gradle (Kotlin DSL)

```kotlin
// Core library
testImplementation("io.flowinquiry.testcontainers:spring-testcontainers:0.9.0")

// Add one or more of the following database modules
testImplementation("io.flowinquiry.testcontainers:postgresql:0.9.0") // PostgreSQL support
testImplementation("io.flowinquiry.testcontainers:mysql:0.9.0") // MySQL support

// Corresponding TestContainers dependencies
testImplementation("org.testcontainers:postgresql:1.21.0")
testImplementation("org.testcontainers:mysql:1.21.0")
testImplementation("io.flowinquiry.testcontainers:postgresql:0.9.1") // PostgreSQL support
testImplementation("io.flowinquiry.testcontainers:mysql:0.9.1") // MySQL support
testImplementation("io.flowinquiry.testcontainers:ollama:0.9.1") // Ollama support
```

### Maven

```xml
<!-- Core library -->
<dependency>
<groupId>io.flowinquiry.testcontainers</groupId>
<artifactId>spring-testcontainers</artifactId>
<version>0.9.0</version>
<scope>test</scope>
</dependency>

<!-- Add one or more of the following database modules -->

<!-- Add this dependency to test Postgres database -->
<dependency>
<groupId>io.flowinquiry.testcontainers</groupId>
<artifactId>postgresql</artifactId>
Expand All @@ -162,27 +166,21 @@ testImplementation("org.testcontainers:mysql:1.21.0")
</dependency>
<dependency>

<groupId>io.flowinquiry.testcontainers</groupId>
<artifactId>mysql</artifactId>
<version>0.9.0</version>
<scope>test</scope>
</dependency>

<!-- TestContainers dependencies -->
<!-- Add this dependency to test MySQL database -->
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>postgresql</artifactId>
<version>1.21.0</version>
<groupId>io.flowinquiry.testcontainers</groupId>
<artifactId>mysql</artifactId>
<version>0.9.1</version>
<scope>test</scope>
</dependency>

<!-- Add this dependency to test Ollama container -->
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>mysql</artifactId>
<version>1.21.0</version>
<groupId>io.flowinquiry.testcontainers</groupId>
<artifactId>ollama</artifactId>
<version>0.9.1</version>
<scope>test</scope>
</dependency>

```

> 📝 As more databases are supported, simply add the corresponding module and TestContainers dependency.
Expand Down Expand Up @@ -220,12 +218,13 @@ class SpringBootPostgresTest {
}
```

## Supported Databases
## Supported Test Containers

Currently, the following databases are supported:
Currently, the following containers are supported:

- PostgreSQL
- MySQL
- Ollama

## Examples

Expand All @@ -237,6 +236,7 @@ The project includes several example modules demonstrating how to use Spring-Tes

* Show how to integrate containerized databases with minimal configuration


### [spring-postgresql](examples/spring-postgresql)

* Spring Framework (no Boot) setup with JPA and PostgreSQL
Expand All @@ -245,6 +245,12 @@ The project includes several example modules demonstrating how to use Spring-Tes

These examples provide a good starting point for integrating Spring-TestContainers into your own projects.

### [springboot-ollama](examples/springboot-ollama)

* Spring Boot applications using Spring AI and Ollama

* Show how to test AI prompts with Ollama container

## Contributing

Contributions are welcome! If you'd like to add support for additional databases or improve the library, please:
Expand Down
1 change: 0 additions & 1 deletion examples/spring-postgresql/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ repositories {
}

dependencies {
implementation(project(":spring-testcontainers"))
implementation(project(":modules:postgresql"))
implementation(platform(libs.spring.bom))
implementation(libs.testcontainers.jdbc)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = PostgresqlConfig.class)
@EnablePostgreSQL
@EnablePostgreSQL(dockerImage = "postgres", version = "16.3")
@ActiveProfiles("test")
public class PostgresqlTest {

Expand Down
4 changes: 1 addition & 3 deletions examples/springboot-mysql/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,12 @@ dependencies {
implementation(project(":modules:mysql"))
implementation(platform(libs.spring.bom))
implementation(platform(libs.spring.boot.bom))
implementation(libs.testcontainers.jdbc)
implementation(libs.testcontainers.mysql)
implementation(libs.slf4j.api)
implementation(libs.logback.classic)
implementation(libs.spring.boot.starter)
implementation(libs.spring.boot.starter.data.jpa)
implementation(libs.spring.boot.autoconfigure)
runtimeOnly("mysql:mysql-connector-java:8.0.33")
runtimeOnly(libs.mysql)
testImplementation(platform(libs.junit.bom))
testImplementation(libs.junit.jupiter)
testImplementation(libs.junit.platform.launcher)
Expand Down
37 changes: 37 additions & 0 deletions examples/springboot-ollama/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
plugins {
id("buildlogic.java-application-conventions")
alias(libs.plugins.spring.dependency.management)
}

repositories {
mavenCentral()
maven { url = uri("https://repo.spring.io/milestone") }
maven { url = uri("https://repo.spring.io/snapshot") }
}

dependencies {
implementation(project(":spring-testcontainers"))
implementation(project(":modules:ollama"))
implementation(platform(libs.spring.bom))
implementation(platform(libs.spring.boot.bom))
implementation(libs.testcontainers.ollama)
implementation(libs.slf4j.api)
implementation(libs.logback.classic)
implementation(libs.spring.boot.starter)
implementation(libs.spring.boot.starter.web)
implementation(libs.spring.boot.autoconfigure)
implementation(platform(libs.spring.ai.bom))
implementation(libs.bundles.spring.ai)
testImplementation(platform(libs.junit.bom))
testImplementation(libs.junit.jupiter)
testImplementation(libs.junit.platform.launcher)
testImplementation(libs.spring.boot.starter.test)
}

tasks.test {
useJUnitPlatform()
}

application {
mainClass.set("io.flowinquiry.testcontainers.examples.ollama.OllamaDemoApp")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package io.flowinquiry.testcontainers.examples.ollama;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/** Spring Boot application for the Ollama AI demo. */
@SpringBootApplication
public class OllamaDemoApp {

public static void main(String[] args) {
SpringApplication.run(OllamaDemoApp.class, args);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package io.flowinquiry.testcontainers.examples.ollama.controller;

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

/** REST controller for interacting with Ollama AI chat functionality. */
@RestController
@RequestMapping("/api/chat")
public class ChatController {

private final ChatClient chatClient;

@Autowired
public ChatController(ChatClient.Builder chatClientBuilder) {
this.chatClient = chatClientBuilder.build();
}

/**
* Endpoint to generate a chat response from Ollama.
*
* @param message The user's message to send to Ollama
* @return The AI-generated response
*/
@GetMapping
public String chat(@RequestParam String message) {
return chatClient.prompt().user(message).call().content();
}

/**
* Simple health check endpoint.
*
* @return A status message
*/
@GetMapping("/health")
public String health() {
return "Ollama Chat Controller is up and running!";
}
}
21 changes: 21 additions & 0 deletions examples/springboot-ollama/src/main/resources/application.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Server configuration
server:
port: 8080

# Ollama configuration
spring:
ai:
ollama:
base-url: http://localhost:11434
chat:
model: llama2
options:
temperature: "0.7"
top-p: "0.9"

# Logging
logging:
level:
root: INFO
org.springframework.ai: DEBUG
io.flowinquiry.testcontainers: DEBUG
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package io.flowinquiry.testcontainers.examples.ollama;

import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.slf4j.LoggerFactory.getLogger;

import io.flowinquiry.testcontainers.ai.EnableOllamaContainer;
import io.flowinquiry.testcontainers.ai.OllamaOptions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.test.web.server.LocalServerPort;
import org.springframework.test.context.ActiveProfiles;

@SpringBootTest(
classes = OllamaDemoApp.class,
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@EnableOllamaContainer(
dockerImage = "ollama/ollama",
version = "0.9.0",
model = "smollm2:135m",
options = @OllamaOptions(temperature = "0.7", topP = "0.5"))
@ActiveProfiles("test")
public class OllamaDemoAppTest {

private static final Logger log = getLogger(OllamaDemoAppTest.class);

@LocalServerPort private int port;

@Autowired private TestRestTemplate restTemplate;

@Autowired private ChatClient.Builder chatClientBuilder;

private ChatClient chatClient;

@BeforeEach
public void init() {
this.chatClient = this.chatClientBuilder.build();
}

@Test
public void testHealthEndpoint() {
log.info("Testing health endpoint");
String url = "http://localhost:" + port + "/api/chat/health";
String response = restTemplate.getForObject(url, String.class);
log.info("Health endpoint response: {}", response);
assertNotNull(response);
assertTrue(response.contains("Ollama Chat Controller is up and running"));
}

@Test
public void testChatClient() {
log.info("Testing chat client directly");
String prompt = "What is Spring AI?";
log.info("Sending prompt: {}", prompt);

String content = chatClient.prompt().user(prompt).call().content();

log.info("Received response: {}", content);
assertNotNull(content);
assertFalse(content.isEmpty());
}
}
10 changes: 10 additions & 0 deletions examples/springboot-ollama/src/test/resources/application-test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Test-specific Ollama configuration
# The base URL will be automatically set by the container

# Logging for tests
logging:
level:
root: INFO
org.springframework.ai: DEBUG
io.flowinquiry.testcontainers: DEBUG
org.testcontainers: INFO
Loading
Loading