--- title: Testing Quarkus applications with Testcontainers linkTitle: Quarkus description: Learn how to test a Quarkus REST API using Testcontainers with PostgreSQL, Hibernate ORM with Panache, and REST Assured. keywords: testcontainers, java, quarkus, testing, postgresql, rest api, rest assured, panache, dev services summary: | Learn how to create a Quarkus REST API with Hibernate ORM with Panache and PostgreSQL, then test it using Quarkus Dev Services, Testcontainers, and REST Assured. aliases: - /guides/testcontainers-java-quarkus/create-project/ - /guides/testcontainers-java-quarkus/run-tests/ - /guides/testcontainers-java-quarkus/write-tests/ params: tags: [testing] time: 25 minutes --- In this guide, you'll learn how to: - Create a Quarkus application with REST API endpoints - Use Hibernate ORM with Panache and PostgreSQL for persistence - Test the REST API using Quarkus Dev Services, which uses Testcontainers behind the scenes - Test with services not supported by Dev Services using `QuarkusTestResourceLifecycleManager` ## 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 Quarkus project ### Set up the project Create a Quarkus project from [code.quarkus.io](https://code.quarkus.io/) by selecting the **RESTEasy Classic**, **RESTEasy Classic Jackson**, **Hibernate Validator**, **Hibernate ORM with Panache**, **JDBC Driver - PostgreSQL**, and **Flyway** extensions. Alternatively, clone the [guide repository](https://github.com/testcontainers/tc-guide-testcontainers-in-quarkus-applications). The key dependencies in `pom.xml` are: ```xml 3.22.3 io.quarkus quarkus-hibernate-orm-panache io.quarkus quarkus-flyway io.quarkus quarkus-hibernate-validator io.quarkus quarkus-resteasy io.quarkus quarkus-resteasy-jackson io.quarkus quarkus-jdbc-postgresql io.quarkus quarkus-junit5 test io.rest-assured rest-assured test ``` ### Create the JPA entity Hibernate ORM with Panache supports the Active Record pattern and the Repository pattern to simplify JPA usage. This guide uses the Active Record pattern. Create `Customer.java` by extending `PanacheEntity`. This gives the entity built-in persistence methods such as `persist()`, `listAll()`, and `findById()`. ```java package com.testcontainers.demo; import io.quarkus.hibernate.orm.panache.PanacheEntity; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.Table; @Entity @Table(name = "customers") public class Customer extends PanacheEntity { @Column(nullable = false) public String name; @Column(nullable = false, unique = true) public String email; public Customer() {} public Customer(Long id, String name, String email) { this.id = id; this.name = name; this.email = email; } } ``` ### Create the CustomerService CDI bean Create a `CustomerService` class annotated with `@ApplicationScoped` and `@Transactional` to handle persistence operations: ```java package com.testcontainers.demo; import jakarta.enterprise.context.ApplicationScoped; import jakarta.transaction.Transactional; import java.util.List; @ApplicationScoped @Transactional public class CustomerService { public List getAll() { return Customer.listAll(); } public Customer create(Customer customer) { customer.persist(); return customer; } } ``` ### Add the Flyway database migration script Create `src/main/resources/db/migration/V1__init_database.sql`: ```sql create sequence customers_seq start with 1 increment by 50; create table customers ( id bigint DEFAULT nextval('customers_seq') not null, name varchar not null, email varchar not null, primary key (id) ); insert into customers(name, email) values ('john', 'john@mail.com'), ('rambo', 'rambo@mail.com'); ``` Enable Flyway migrations in `src/main/resources/application.properties`: ```properties quarkus.flyway.migrate-at-start=true ``` ### Create the REST API endpoints Create `CustomerResource.java` with endpoints for fetching all customers and creating a customer: ```java package com.testcontainers.demo; import jakarta.ws.rs.Consumes; import jakarta.ws.rs.GET; import jakarta.ws.rs.POST; import jakarta.ws.rs.Path; import jakarta.ws.rs.Produces; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; import java.util.List; @Path("/api/customers") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) public class CustomerResource { private final CustomerService customerService; public CustomerResource(CustomerService customerService) { this.customerService = customerService; } @GET public List getAllCustomers() { return customerService.getAll(); } @POST public Response createCustomer(Customer customer) { var savedCustomer = customerService.create(customer); return Response.status(Response.Status.CREATED).entity(savedCustomer).build(); } } ``` ## Write tests with Testcontainers ### Quarkus Dev Services Quarkus Dev Services automatically provisions unconfigured services in development and test mode. When you include an extension and don't configure it, Quarkus starts the relevant service using [Testcontainers](https://www.testcontainers.org/) behind the scenes and wires the application to use that service. > [!NOTE] > Dev Services requires a > [supported Docker environment](https://www.testcontainers.org/supported_docker_environment/). Quarkus Dev Services supports most commonly used services like SQL databases, Kafka, RabbitMQ, Redis, and MongoDB. For more information, see the [Quarkus Dev Services guide](https://quarkus.io/guides/dev-services). ### Write tests for the API endpoints Test the `GET /api/customers` and `POST /api/customers` endpoints using REST Assured. The `io.rest-assured:rest-assured` library was already added as a test dependency when you generated the project. Create `CustomerResourceTest.java` and annotate it with `@QuarkusTest`. This bootstraps the application along with the required services using Dev Services. Because you haven't configured datasource properties, Dev Services automatically starts a PostgreSQL database using Testcontainers. ```java package com.testcontainers.demo; import static io.restassured.RestAssured.given; import static org.hamcrest.CoreMatchers.is; import static org.junit.jupiter.api.Assertions.assertFalse; import io.quarkus.test.junit.QuarkusTest; import io.restassured.common.mapper.TypeRef; import io.restassured.http.ContentType; import java.util.List; import org.junit.jupiter.api.Test; @QuarkusTest class CustomerResourceTest { @Test void shouldGetAllCustomers() { List customers = given().when() .get("/api/customers") .then() .statusCode(200) .extract() .as(new TypeRef<>() {}); assertFalse(customers.isEmpty()); } @Test void shouldCreateCustomerSuccessfully() { Customer customer = new Customer(null, "John", "john@gmail.com"); given().contentType(ContentType.JSON) .body(customer) .when() .post("/api/customers") .then() .statusCode(201) .body("name", is("John")) .body("email", is("john@gmail.com")); } } ``` Here's what the test does: - `@QuarkusTest` starts the full Quarkus application with Dev Services enabled. - Dev Services starts a PostgreSQL container using Testcontainers and configures the datasource automatically. - `shouldGetAllCustomers()` calls `GET /api/customers` and verifies that seeded data from the Flyway migration is returned. - `shouldCreateCustomerSuccessfully()` sends a `POST /api/customers` request and verifies the response contains the created customer data. ### Customize test configuration By default, the Quarkus test instance starts on port 8081 and uses a `postgres:14` Docker image. Customize both by adding these properties to `src/main/resources/application.properties`: ```properties quarkus.http.test-port=0 quarkus.datasource.devservices.image-name=postgres:15.2-alpine ``` Setting `quarkus.http.test-port=0` starts the application on a random available port, avoiding port conflicts. The `devservices.image-name` property lets you pin the PostgreSQL image to a specific version that matches production. ### Test with services not supported by Dev Services Your application might use a service that Dev Services doesn't support out of the box. In that case, use `QuarkusTestResourceLifecycleManager` to start the service before the Quarkus application starts for testing. For example, suppose the application uses CockroachDB. First, add the CockroachDB Testcontainers module dependency: ```xml org.testcontainers cockroachdb test ``` Create a `CockroachDBTestResource` that implements `QuarkusTestResourceLifecycleManager`: ```java package com.testcontainers.demo; import io.quarkus.test.common.QuarkusTestResourceLifecycleManager; import java.util.HashMap; import java.util.Map; import org.testcontainers.containers.CockroachContainer; public class CockroachDBTestResource implements QuarkusTestResourceLifecycleManager { CockroachContainer cockroachdb; @Override public Map start() { cockroachdb = new CockroachContainer("cockroachdb/cockroach:v22.2.0"); cockroachdb.start(); Map conf = new HashMap<>(); conf.put("quarkus.datasource.jdbc.url", cockroachdb.getJdbcUrl()); conf.put("quarkus.datasource.username", cockroachdb.getUsername()); conf.put("quarkus.datasource.password", cockroachdb.getPassword()); return conf; } @Override public void stop() { cockroachdb.stop(); } } ``` Use the `CockroachDBTestResource` with `@QuarkusTestResource` in a test class: ```java package com.testcontainers.demo; import static io.restassured.RestAssured.given; import static org.junit.jupiter.api.Assertions.assertFalse; import io.quarkus.test.common.QuarkusTestResource; import io.quarkus.test.junit.QuarkusTest; import io.restassured.common.mapper.TypeRef; import java.util.List; import org.junit.jupiter.api.Test; @QuarkusTest @QuarkusTestResource(value = CockroachDBTestResource.class, restrictToAnnotatedClass = true) class CockroachDBTest { @Test void shouldGetAllCustomers() { List customers = given().when() .get("/api/customers") .then() .statusCode(200) .extract() .as(new TypeRef<>() {}); assertFalse(customers.isEmpty()); } } ``` The `restrictToAnnotatedClass = true` attribute ensures the CockroachDB container only starts when running this specific test class, rather than being activated for all tests. ## Run tests and next steps ### Run the tests ```console $ ./mvnw test ``` Or with Gradle: ```console $ ./gradlew test ``` You should see the PostgreSQL Docker container start and all tests pass. After the tests finish, the container stops and is removed automatically. ### Run the application locally Quarkus Dev Services automatically provisions unconfigured services in development mode. Start the Quarkus application in dev mode: ```console $ ./mvnw compile quarkus:dev ``` Or with Gradle: ```console $ ./gradlew quarkusDev ``` Dev Services starts a PostgreSQL container automatically. If you're running a PostgreSQL database on your system and want to use that instead, configure the datasource properties in `src/main/resources/application.properties`: ```properties quarkus.datasource.jdbc.url=jdbc:postgresql://localhost:5432/postgres quarkus.datasource.username=postgres quarkus.datasource.password=postgres ``` When these properties are set explicitly, Dev Services doesn't provision the database container and instead connects to the configured database. ### Summary Quarkus Dev Services improves the developer experience by automatically provisioning the required services using Testcontainers during development and testing. This guide covered: - Building a REST API using JAX-RS with Hibernate ORM with Panache - Testing API endpoints using REST Assured with Dev Services handling database provisioning - Using `QuarkusTestResourceLifecycleManager` for services not supported by Dev Services - Running the application locally with Dev Services To learn more about Testcontainers, visit the [Testcontainers overview](https://testcontainers.com/getting-started/). ### Further reading - [Quarkus Dev Services overview](https://quarkus.io/guides/dev-services) - [Quarkus testing guide](https://quarkus.io/guides/getting-started-testing) - [Testcontainers Postgres module](https://java.testcontainers.org/modules/databases/postgres/)