---
title: Testing a Spring Boot REST API with Testcontainers
linkTitle: Spring Boot REST API
description: Learn how to test a Spring Boot REST API using Testcontainers with PostgreSQL and REST Assured.
keywords: testcontainers, java, spring boot, testing, postgresql, rest api, rest assured, jpa
summary: |
Learn how to create a Spring Boot REST API with Spring Data JPA and PostgreSQL,
then test it using Testcontainers and REST Assured.
aliases:
- /guides/testcontainers-java-spring-boot-rest-api/create-project/
- /guides/testcontainers-java-spring-boot-rest-api/run-tests/
- /guides/testcontainers-java-spring-boot-rest-api/write-tests/
params:
tags: [testing]
time: 25 minutes
---
In this guide, you will learn how to:
- Create a Spring Boot application with a REST API endpoint
- Use Spring Data JPA with PostgreSQL to store and retrieve data
- Test the REST API using Testcontainers and REST Assured
## Prerequisites
- Java 17+
- Maven or Gradle
- A Docker environment supported by Testcontainers
> [!NOTE]
> If you're new to Testcontainers, visit the
> [Testcontainers overview](https://testcontainers.com/getting-started/) to learn more about
> Testcontainers and the benefits of using it.
## Create the Spring Boot project
### Set up the project
Create a Spring Boot project from [Spring Initializr](https://start.spring.io)
by selecting the **Spring Web**, **Spring Data JPA**, **PostgreSQL Driver**, and
**Testcontainers** starters.
Alternatively, clone the
[guide repository](https://github.com/testcontainers/tc-guide-testing-spring-boot-rest-api).
The key dependencies in `pom.xml` are:
```xml
17
2.0.4
org.springframework.boot
spring-boot-starter-data-jpa
org.springframework.boot
spring-boot-starter-web
org.postgresql
postgresql
runtime
org.springframework.boot
spring-boot-starter-test
test
org.testcontainers
testcontainers-junit-jupiter
test
org.testcontainers
testcontainers-postgresql
test
io.rest-assured
rest-assured
test
```
Using the Testcontainers BOM (Bill of Materials) is recommended so that you
don't have to repeat the version for every Testcontainers module dependency.
### Create the JPA entity
Create `Customer.java`:
```java
package com.testcontainers.demo;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
@Entity
@Table(name = "customers")
class Customer {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String name;
@Column(nullable = false, unique = true)
private String email;
public Customer() {}
public Customer(Long id, String name, String email) {
this.id = id;
this.name = name;
this.email = email;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
```
### Create the Spring Data JPA repository
```java
package com.testcontainers.demo;
import org.springframework.data.jpa.repository.JpaRepository;
interface CustomerRepository extends JpaRepository {}
```
### Add the schema creation script
Create `src/main/resources/schema.sql`:
```sql
create table if not exists customers (
id bigserial not null,
name varchar not null,
email varchar not null,
primary key (id),
UNIQUE (email)
);
```
Enable schema initialization in `src/main/resources/application.properties`:
```properties
spring.sql.init.mode=always
```
### Create the REST API endpoint
Create `CustomerController.java`:
```java
package com.testcontainers.demo;
import java.util.List;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
class CustomerController {
private final CustomerRepository repo;
CustomerController(CustomerRepository repo) {
this.repo = repo;
}
@GetMapping("/api/customers")
List getAll() {
return repo.findAll();
}
}
```
## Write tests with Testcontainers
To test the REST API, you need a running Postgres database and a started
Spring context. Testcontainers spins up Postgres in a Docker container and
`@DynamicPropertySource` connects it to Spring.
### Write the test
Create `CustomerControllerTest.java`:
```java
package com.testcontainers.demo;
import static io.restassured.RestAssured.given;
import static org.hamcrest.Matchers.hasSize;
import io.restassured.RestAssured;
import io.restassured.http.ContentType;
import java.util.List;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.server.LocalServerPort;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;
import org.testcontainers.postgresql.PostgreSQLContainer;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class CustomerControllerTest {
@LocalServerPort
private Integer port;
static PostgreSQLContainer postgres = new PostgreSQLContainer(
"postgres:16-alpine"
);
@BeforeAll
static void beforeAll() {
postgres.start();
}
@AfterAll
static void afterAll() {
postgres.stop();
}
@DynamicPropertySource
static void configureProperties(DynamicPropertyRegistry registry) {
registry.add("spring.datasource.url", postgres::getJdbcUrl);
registry.add("spring.datasource.username", postgres::getUsername);
registry.add("spring.datasource.password", postgres::getPassword);
}
@Autowired
CustomerRepository customerRepository;
@BeforeEach
void setUp() {
RestAssured.baseURI = "http://localhost:" + port;
customerRepository.deleteAll();
}
@Test
void shouldGetAllCustomers() {
List customers = List.of(
new Customer(null, "John", "john@mail.com"),
new Customer(null, "Dennis", "dennis@mail.com")
);
customerRepository.saveAll(customers);
given()
.contentType(ContentType.JSON)
.when()
.get("/api/customers")
.then()
.statusCode(200)
.body(".", hasSize(2));
}
}
```
Here's what the test does:
- `@SpringBootTest` starts the full application on a random port.
- A `PostgreSQLContainer` starts in `@BeforeAll` and stops in `@AfterAll`.
- `@DynamicPropertySource` registers the container's JDBC URL, username, and
password with Spring so that the datasource connects to the test container.
- `@BeforeEach` deletes all customer rows before each test to prevent test
pollution.
- `shouldGetAllCustomers()` inserts two customers, calls `GET /api/customers`,
and verifies the response contains 2 records.
## Run tests and next steps
### Run the tests
```console
$ ./mvnw test
```
Or with Gradle:
```console
$ ./gradlew test
```
You should see the Postgres Docker container start and all tests pass. After
the tests finish, the container stops and is removed automatically.
### Summary
The Testcontainers library helps you write integration tests by using the same
type of database (Postgres) that you use in production, instead of mocks or
in-memory databases. Because you test against real services, you're free to
refactor code and still verify that the application works as expected.
To learn more about Testcontainers, visit the
[Testcontainers overview](https://testcontainers.com/getting-started/).
### Further reading
- [Testcontainers JUnit 5 quickstart](https://java.testcontainers.org/quickstart/junit_5_quickstart/)
- [Testcontainers Postgres module](https://java.testcontainers.org/modules/databases/postgres/)
- [Testcontainers JDBC support](https://java.testcontainers.org/modules/databases/jdbc/)