Partner – Microsoft – NPI EA (cat = Baeldung)
announcement - icon

Azure Container Apps is a fully managed serverless container service that enables you to build and deploy modern, cloud-native Java applications and microservices at scale. It offers a simplified developer experience while providing the flexibility and portability of containers.

Of course, Azure Container Apps has really solid support for our ecosystem, from a number of build options, managed Java components, native metrics, dynamic logger, and quite a bit more.

To learn more about Java features on Azure Container Apps, visit the documentation page.

You can also ask questions and leave feedback on the Azure Container Apps GitHub page.

Partner – Microsoft – NPI EA (cat= Spring Boot)
announcement - icon

Azure Container Apps is a fully managed serverless container service that enables you to build and deploy modern, cloud-native Java applications and microservices at scale. It offers a simplified developer experience while providing the flexibility and portability of containers.

Of course, Azure Container Apps has really solid support for our ecosystem, from a number of build options, managed Java components, native metrics, dynamic logger, and quite a bit more.

To learn more about Java features on Azure Container Apps, you can get started over on the documentation page.

And, you can also ask questions and leave feedback on the Azure Container Apps GitHub page.

Partner – Orkes – NPI EA (cat=Spring)
announcement - icon

Modern software architecture is often broken. Slow delivery leads to missed opportunities, innovation is stalled due to architectural complexities, and engineering resources are exceedingly expensive.

Orkes is the leading workflow orchestration platform built to enable teams to transform the way they develop, connect, and deploy applications, microservices, AI agents, and more.

With Orkes Conductor managed through Orkes Cloud, developers can focus on building mission critical applications without worrying about infrastructure maintenance to meet goals and, simply put, taking new products live faster and reducing total cost of ownership.

Try a 14-Day Free Trial of Orkes Conductor today.

Partner – Orkes – NPI EA (tag=Microservices)
announcement - icon

Modern software architecture is often broken. Slow delivery leads to missed opportunities, innovation is stalled due to architectural complexities, and engineering resources are exceedingly expensive.

Orkes is the leading workflow orchestration platform built to enable teams to transform the way they develop, connect, and deploy applications, microservices, AI agents, and more.

With Orkes Conductor managed through Orkes Cloud, developers can focus on building mission critical applications without worrying about infrastructure maintenance to meet goals and, simply put, taking new products live faster and reducing total cost of ownership.

Try a 14-Day Free Trial of Orkes Conductor today.

eBook – Guide Spring Cloud – NPI EA (cat=Spring Cloud)
announcement - icon

Let's get started with a Microservice Architecture with Spring Cloud:

>> Join Pro and download the eBook

eBook – Mockito – NPI EA (tag = Mockito)
announcement - icon

Mocking is an essential part of unit testing, and the Mockito library makes it easy to write clean and intuitive unit tests for your Java code.

Get started with mocking and improve your application tests using our Mockito guide:

Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Reactive – NPI EA (cat=Reactive)
announcement - icon

Spring 5 added support for reactive programming with the Spring WebFlux module, which has been improved upon ever since. Get started with the Reactor project basics and reactive programming in Spring Boot:

>> Join Pro and download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Jackson – NPI EA (cat=Jackson)
announcement - icon

Do JSON right with Jackson

Download the E-book

eBook – HTTP Client – NPI EA (cat=Http Client-Side)
announcement - icon

Get the most out of the Apache HTTP Client

Download the E-book

eBook – Maven – NPI EA (cat = Maven)
announcement - icon

Get Started with Apache Maven:

Download the E-book

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

eBook – RwS – NPI EA (cat=Spring MVC)
announcement - icon

Building a REST API with Spring?

Download the E-book

Course – LS – NPI EA (cat=Jackson)
announcement - icon

Get started with Spring and Spring Boot, through the Learn Spring course:

>> LEARN SPRING
Course – RWSB – NPI EA (cat=REST)
announcement - icon

Explore Spring Boot 3 and Spring 6 in-depth through building a full REST API with the framework:

>> The New “REST With Spring Boot”

Course – LSS – NPI EA (cat=Spring Security)
announcement - icon

Yes, Spring Security can be complex, from the more advanced functionality within the Core to the deep OAuth support in the framework.

I built the security material as two full courses - Core and OAuth, to get practical with these more complex scenarios. We explore when and how to use each feature and code through it on the backing project.

You can explore the course here:

>> Learn Spring Security

Course – LSD – NPI EA (tag=Spring Data JPA)
announcement - icon

Spring Data JPA is a great way to handle the complexity of JPA with the powerful simplicity of Spring Boot.

Get started with Spring Data JPA through the guided reference course:

>> CHECK OUT THE COURSE

Partner – LambdaTest – NPI EA (cat=Testing)
announcement - icon

Accessibility testing is a crucial aspect to ensure that your application is usable for everyone and meets accessibility standards that are required in many countries.

By automating these tests, teams can quickly detect issues related to screen reader compatibility, keyboard navigation, color contrast, and other aspects that could pose a barrier to using the software effectively for people with disabilities.

Learn how to automate accessibility testing with Selenium and the LambdaTest cloud-based testing platform that lets developers and testers perform accessibility automation on over 3000+ real environments:

Automated Accessibility Testing With Selenium

Partner – LambdaTest – NPI (cat= Testing)
announcement - icon

Accessibility testing is a crucial aspect to ensure that your application is usable for everyone and meets accessibility standards that are required in many countries.

By automating these tests, teams can quickly detect issues related to screen reader compatibility, keyboard navigation, color contrast, and other aspects that could pose a barrier to using the software effectively for people with disabilities.

Learn how to automate accessibility testing with Selenium and the LambdaTest cloud-based testing platform that lets developers and testers perform accessibility automation on over 3000+ real environments:

Automated Accessibility Testing With Selenium

1. Overview

Quarkus, the Supersonic Subatomic Java, promises to deliver small artifacts, extremely fast boot time, and lower time-to-first-request. We can understand it as a framework that integrates Java standard technologies (Jakarta EE, MicroProfile, and others) and enables building a standalone application that can be deployed in any container runtime, easily fulfilling the requirements of cloud-native applications.

In this article, we’ll learn how to implement integration tests with Citrus, a framework written by Christoph Deppisch – Principal Software Engineer at Red Hat.

2. The Purpose of Citrus

The applications we develop typically don’t run isolated but communicate with other systems, such as databases, messaging systems, or online services. When testing our application, we could do this in an isolated manner by mocking the corresponding objects. But we also might want to test the communication of our application with external systems. That’s where Citrus comes into play.

Let’s take a closer look at the most common interaction scenarios.

2.1. HTTP

Our web application may have an HTTP-based API (e.g., a REST API). Citrus can act as an HTTP client that calls our application’s HTTP API and verifies the response (like REST-assured does). Our application might also be a consumer of another application’s HTTP API. Citrus could run an embedded HTTP server and act as a mock in this case:

 

quarkus citrus 01 http

2.2. Kafka

In this case, our application is a Kafka consumer. Citrus can act as a Kafka producer to send a record to a topic so that our application gets triggered by consuming the record. Our application also might be a Kafka producer.

Citrus can act as a consumer to verify the messages that our application sent to the topic during the test. Additionally, Citrus provides an embedded Kafka server to be independent of any external server during the test:

 

quarkus citrus 02 kafka

2.3. Relational Databases

Our application uses a relational database. Citrus can act as a JDBC client that verifies that the database has the expected state. Furthermore, Citrus provides a JDBC driver and an embedded database mock that can be instrumented to return test-case-specific results and verify the executed database queries:

 

quarkus citrus 03 jdbc

2.4. Further Support

Citrus supports further external systems, such as REST, SOAP, JMS, Websocket, Mail, FTP, and Apache Camel endpoints. We can find a full listing in the documentation.

3. Citrus Tests With Quarkus

Quarkus has extensive support for writing integration tests, including mocking, test profiles, and testing native executables. Citrus provides the QuarkusTest runtime, a Quarkus Test Resource to extend Quarkus-based tests by including Citrus capabilities.

Let’s have a sample where we use the most common technologies – a REST service provider, that stores data in a relational database and sends a message to Kafka when a new item is created. For Citrus, it doesn’t matter how we implement this in detail. Our application is a black box, and only the external systems and the communication channels are crucial:

 

quarkus citrus 10 appsample

3.1. Maven Dependencies

To use Citrus in our Quarkus-based project, we can use the citrus-bom:

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.citrusframework</groupId>
            <artifactId>citrus-bom</artifactId>
            <version>4.2.1</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
<dependencies>
    <dependency>
        <groupId>org.citrusframework</groupId>
        <artifactId>citrus-quarkus</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

We can optionally add further modules, depending on the technologies used:

<dependency>
    <groupId>org.citrusframework</groupId>
    <artifactId>citrus-openapi</artifactId>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.citrusframework</groupId>
    <artifactId>citrus-http</artifactId>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.citrusframework</groupId>
    <artifactId>citrus-validation-json</artifactId>
</dependency>
<dependency>
    <groupId>org.citrusframework</groupId>
    <artifactId>citrus-validation-hamcrest</artifactId>
    <version>${citrus.version}</version>
</dependency>
<dependency>
    <groupId>org.citrusframework</groupId>
    <artifactId>citrus-sql</artifactId>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.citrusframework</groupId>
    <artifactId>citrus-kafka</artifactId>
    <scope>test</scope>
</dependency>

3.2. Application Configuration

There isn’t any global Quarkus configuration that needs to be done for Citrus. There’s just a warning in the logs about split packages, that we can avoid by adding this line to the application.properties file:

%test.quarkus.arc.ignored-split-packages=org.citrusframework.*

3.3. Test Setup for the Boundary

A typical test with Citrus would have the elements:

  • a @CitrusSupport annotation that adds the Quarkus Test Resource to extend the Quarkus-based test processing
  • a @CitrusConfiguration annotation that includes one or multiple configuration classes for Citrus, that are used for the global configuration of communication endpoints and dependency injection into the test classes
  • fields to get endpoints and other Citrus-provided objects injected

So, if we want to test the boundary, we would need an HTTP client to send a request to our application and verify the response. First, we need to create the Citrus configuration class:

public class BoundaryCitrusConfig {

    public static final String API_CLIENT = "apiClient";

    @BindToRegistry(name = API_CLIENT)
    public HttpClient apiClient() {
        return http()
          .client()
          .requestUrl("http://localhost:8081")
          .build();
    }

}

Then, we create the test class:

@QuarkusTest
@CitrusSupport
@CitrusConfiguration(classes = {
    BoundaryCitrusConfig.class
})
class CitrusTests {

    @CitrusEndpoint(name = BoundaryCitrusConfig.API_CLIENT)
    HttpClient apiClient;

}

As a convention, we could skip the name attributes of the annotations if the declaring method and the field in the test class have the same name. This might be shorter, but prone to errors due to missing compiler checks.

3.4. Testing the Boundary

For writing the test, we need to know that Citrus has a declarative concept, defining the components:

quarkus citrus 11 concept

 

  • Test Context is an object that provides test variables and functions, that, among others, replace dynamic content in message payloads and headers.
  • A Test Action is an abstraction for each step in the test. This could be one interaction, like sending a request or receiving a response, including validations and verifications. It could also be just a simple output or a timer. Citrus provides a Java DSL, and XML as an alternative to define test definitions with Test Actions. We can find a list of pre-defined Test Actions in the documentation.
  • Test Action Builder is used to define and build the Test Action. Citrus uses the Builder Pattern here.
  • Test Action Runner uses the Test Action Builder to build the Test Action. Then, it executes the Test Action, providing the Test Context. For BBD style, we can use a GherkinTestActionRunner.

We can get the Test Action Runner injected too. The code shows a test that sends an HTTP POST request to http://localhost:8081/api/v1/todos with a JSON body and expects to receive a response with a 201 status code:

@CitrusResource
GherkinTestActionRunner t;

@Test
void shouldReturn201OnCreateItem() {
    t.when(
            http()
              .client(apiClient)
              .send()
              .post("/api/v1/todos")
              .message()
              .contentType(MediaType.APPLICATION_JSON)
              .body("{\"title\": \"test\"}")
    );
    t.then(
            http()
              .client(apiClient)
              .receive()
              .response(HttpStatus.CREATED)
    );
}

The body is written directly as a JSON string. Alternatively, we could use a Data Dictionary as shown in this sample.

For message validation, we have multiple possibilities. For example, using JSON-Path in combination with Hamcrest, we can extend the then block:

t.then(
        http()
          .client(apiClient)
          .receive()
          .response(HttpStatus.CREATED)
          .message()
          .type(MessageType.JSON)
          .validate(
                    jsonPath()
                      .expression("$.title", "test")
                      .expression("$.id", is(notNullValue()))
          )
);

Unfortunately, only Hamcrest is supported. For AssertJ, there’s been a GitHub issue opened in 2016.

3.5. Testing the Boundary Based on OpenAPI

We can also send requests based on an OpenAPI definition. This automatically validates the response concerning property and header constraints declared in the OpenAPI schema.

First, we need to load the OpenAPI schema. For example, if we have a YML file in our project, we can do this by defining an OpenApiSpecification field:

final OpenApiSpecification apiSpecification = OpenApiSpecification.from(
        Resources.create("classpath:openapi.yml")
);

We could also read the OpenApi from the running Quarkus application, if available:

final OpenApiSpecification apiSpecification = OpenApiSpecification.from(
    "http://localhost:8081/q/openapi"
);

For the test, we can refer to the operationId to send a request or to verify a response:

t.when(
        openapi()
          .specification(apiSpecification)
          .client(apiClient)
          .send("createTodo") // operationId
);
t.then(
        openapi()
          .specification(apiSpecification)
          .client(apiClient)
          .receive("createTodo", HttpStatus.CREATED)
);

This generates a request including the necessary body by creating random values. Currently, it’s impossible to use explicitly defined values for headers, parameters, or bodies (see this GitHub-Issue). Also, there is a bug when generating random date values. We could avoid this at least for optional fields by skipping random values:

@BeforeEach
void setup() {
    this.apiSpecification.setGenerateOptionalFields(false);
    this.apiSpecification.setValidateOptionalFields(false);
}

In this case, we must also disable strict validation, which will fail because the service returns optional fields. (see this GitHub-Issue) We could do this by using JUnit Pioneer. For this, we add the junit-pioneer dependency:

<dependency>
    <groupId>org.junit-pioneer</groupId>
    <artifactId>junit-pioneer</artifactId>
    <version>2.2.0</version>
    <scope>test</scope>
</dependency>

Then, we can add the @SystemProperty annotation to our test class before the @CitrusSupport annotation:

@SetSystemProperty(
    key = "citrus.json.message.validation.strict",
    value = "false"
)

3.6. Testing the Database Access

When we invoke the create operation of our REST API, it should store the new item in the database. To evaluate this, we can query the database for the newly created ID.

First, we need a data source. We can get this easily injected from Quarkus:

@Inject
DataSource dataSource;

Then, we need to extract the ID of the newly created item from the response body and store it as a Test Context variable:

t.when(
        http()
          .client(apiClient)
          .send()
          .post("/api/v1/todos")
          .message()
          .contentType(MediaType.APPLICATION_JSON)
          .body("{\"title\": \"test\"}")
);
t.then(
        http()
          .client(apiClient)
          .receive()
          .response(HttpStatus.CREATED)
          // save new id to test context variable "todoId"
          .extract(fromBody().expression("$.id", "todoId"))
);

We can now check the database with a query that uses the variable:

t.then(
        sql()
          .dataSource(dataSource)
          .query()
          .statement("select title from todos where id=${todoId}")
          .validate("title", "test")
);

3.7. Testing the Messaging

When we invoke the create operation of our REST API, it should send the new item to a Kafka topic. To evaluate this, we can subscribe to the topic and consume the message.

For this, we need a Citrus endpoint:

public class KafkaCitrusConfig {

    public static final String TODOS_EVENTS_TOPIC = "todosEvents";

    @BindToRegistry(name = TODOS_EVENTS_TOPIC)
    public KafkaEndpoint todosEvents() {
        return kafka()
          .asynchronous()
          .topic("todo-events")
          .build();
    }

}

Then, we want Citrus to inject this endpoint into our test:

@QuarkusTest
@CitrusSupport
@CitrusConfiguration(classes = {
    BoundaryCitrusConfig.class,
    KafkaCitrusConfig.class
})
class MessagingCitrusTest {

    @CitrusEndpoint(name = KafkaCitrusConfig.TODOS_EVENTS_TOPIC)
    KafkaEndpoint todosEvents;

    // ...

}

After sending and receiving the request as we saw earlier, we can then subscribe to the topic and consume and validate the message:

t.and(
        receive()
          .endpoint(todosEvents)
          .message()
          .type(MessageType.JSON)
          .validate(
                    jsonPath()
                      .expression("$.title", "test")
                      .expression("$.id", "${todoId}")
          )
);

3.8. Mocking the Servers

Citrus can mock external systems. This can be helpful to avoid the need for these external systems for testing purposes and to directly verify the messages sent to these systems and mock the responses instead of validating the state of the system after message processing.

In the case of Kafka, the Quarkus Dev Services feature runs a Docker container with a Kafka server. We could use the Citrus mock instead. We then have to disable the Dev Services feature in the application.properties file:

%test.quarkus.kafka.devservices.enabled=false

Then, we configure the Citrus mock server:

public class EmbeddedKafkaCitrusConfig {

    private EmbeddedKafkaServer kafkaServer;

    @BindToRegistry
    public EmbeddedKafkaServer kafka() {
        if (null == kafkaServer) {
            kafkaServer = new EmbeddedKafkaServerBuilder()
              .kafkaServerPort(9092)
              .topics("todo-events")
              .build();
        }
        return kafkaServer;
    }

    // stop the server after the test
    @BindToRegistry
    public AfterSuite afterSuiteActions() {
        return afterSuite()
          .actions(context -> kafka().stop())
          .build();
    }

}

We could then activate the mock server by just referring to this configuration class as already known:

@QuarkusTest
@CitrusSupport
@CitrusConfiguration(classes = {
    BoundaryCitrusConfig.class,
    KafkaCitrusConfig.class,
    EmbeddedKafkaCitrusConfig.class
})
class MessagingCitrusTest {

    // ...

}

We can also find mock servers for external HTTP services and relational databases.

4. Challenges

Writing tests with Citrus also has challenges. The API isn’t always intuitive. Integration for AssertJ is missing. Citrus throws exceptions instead of AssertionErrors when validation fails, resulting in confusing test reports. The online documentation is extensive, but code samples contain Groovy code, and sometimes XML. There’s a repository with Java code samples in GitHub, that might help. Javadocs are incomplete.

It seems that the integration into the Spring Framework is in focus. The documentation often refers to Citrus configuration in Spring. The citrus-jdbc module depends on Spring Core and Spring JDBC, which we’ll get as unnecessary transitive dependencies for our tests unless we exclude them.

5. Conclusion

In this tutorial, we’ve learned how to implement Quarkus tests with Citrus. Citrus provides many features to test the communication of our application with external systems. This also includes mocking these systems for the test. It’s well-documented, but the included code samples match for other use cases than integration into Quarkus. Fortunately, there is a GitHub repository that contains samples with Quarkus.

The code backing this article is available on GitHub. Once you're logged in as a Baeldung Pro Member, start learning and coding on the project.
Baeldung Pro – NPI EA (cat = Baeldung)
announcement - icon

Baeldung Pro comes with both absolutely No-Ads as well as finally with Dark Mode, for a clean learning experience:

>> Explore a clean Baeldung

Once the early-adopter seats are all used, the price will go up and stay at $33/year.

Partner – Microsoft – NPI EA (cat = Baeldung)
announcement - icon

Azure Container Apps is a fully managed serverless container service that enables you to build and deploy modern, cloud-native Java applications and microservices at scale. It offers a simplified developer experience while providing the flexibility and portability of containers.

Of course, Azure Container Apps has really solid support for our ecosystem, from a number of build options, managed Java components, native metrics, dynamic logger, and quite a bit more.

To learn more about Java features on Azure Container Apps, visit the documentation page.

You can also ask questions and leave feedback on the Azure Container Apps GitHub page.

Partner – Microsoft – NPI EA (cat = Spring Boot)
announcement - icon

Azure Container Apps is a fully managed serverless container service that enables you to build and deploy modern, cloud-native Java applications and microservices at scale. It offers a simplified developer experience while providing the flexibility and portability of containers.

Of course, Azure Container Apps has really solid support for our ecosystem, from a number of build options, managed Java components, native metrics, dynamic logger, and quite a bit more.

To learn more about Java features on Azure Container Apps, visit the documentation page.

You can also ask questions and leave feedback on the Azure Container Apps GitHub page.

Partner – Orkes – NPI EA (cat = Spring)
announcement - icon

Modern software architecture is often broken. Slow delivery leads to missed opportunities, innovation is stalled due to architectural complexities, and engineering resources are exceedingly expensive.

Orkes is the leading workflow orchestration platform built to enable teams to transform the way they develop, connect, and deploy applications, microservices, AI agents, and more.

With Orkes Conductor managed through Orkes Cloud, developers can focus on building mission critical applications without worrying about infrastructure maintenance to meet goals and, simply put, taking new products live faster and reducing total cost of ownership.

Try a 14-Day Free Trial of Orkes Conductor today.

Partner – Orkes – NPI EA (tag = Microservices)
announcement - icon

Modern software architecture is often broken. Slow delivery leads to missed opportunities, innovation is stalled due to architectural complexities, and engineering resources are exceedingly expensive.

Orkes is the leading workflow orchestration platform built to enable teams to transform the way they develop, connect, and deploy applications, microservices, AI agents, and more.

With Orkes Conductor managed through Orkes Cloud, developers can focus on building mission critical applications without worrying about infrastructure maintenance to meet goals and, simply put, taking new products live faster and reducing total cost of ownership.

Try a 14-Day Free Trial of Orkes Conductor today.

eBook – HTTP Client – NPI EA (cat=HTTP Client-Side)
announcement - icon

The Apache HTTP Client is a very robust library, suitable for both simple and advanced use cases when testing HTTP endpoints. Check out our guide covering basic request and response handling, as well as security, cookies, timeouts, and more:

>> Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

Course – LS – NPI EA (cat=REST)

announcement - icon

Get started with Spring Boot and with core Spring, through the Learn Spring course:

>> CHECK OUT THE COURSE

eBook Jackson – NPI EA – 3 (cat = Jackson)