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 – Moderne – NPI EA (cat=Spring Boot)
announcement - icon

Refactor Java code safely — and automatically — with OpenRewrite.

Refactoring big codebases by hand is slow, risky, and easy to put off. That’s where OpenRewrite comes in. The open-source framework for large-scale, automated code transformations helps teams modernize safely and consistently.

Each month, the creators and maintainers of OpenRewrite at Moderne run live, hands-on training sessions — one for newcomers and one for experienced users. You’ll see how recipes work, how to apply them across projects, and how to modernize code with confidence.

Join the next session, bring your questions, and learn how to automate the kind of work that usually eats your sprint time.

Course – LJB – NPI EA (cat = Core Java)
announcement - icon

Code your way through and build up a solid, practical foundation of Java:

>> Learn Java Basics

1. Introduction

In this tutorial, we’ll explore using CATS to automate testing for REST APIs configured with OpenAPI. Writing API tests manually can be tedious and time-consuming, but CATS simplifies the process by automatically generating and running hundreds of tests.

This reduces manual effort and improves API reliability by identifying potential issues early in development. Even for simple APIs, common errors can occur, and CATS helps us find and address them efficiently.

While CATS works with any OpenAPI-annotated application, we’ll demonstrate it using a Spring and Jackson-based application.

2. Testing Made Easy With CATS

CATS stands for Contract Auto Test Service. The contract refers to the OpenAPI specification of our REST API. The Auto Test is a fuzz test with random data and data returned by the API operations in some scenarios (like IDs). It’s an external CLI application that requires access to our API’s URL and its OpenAPI contract, either as a file or URL.

Some of its key features include:

  • Automatic generation and running of tests based on the API Contract
  • Automatic generation of HTML reports detailing test results
  • Simple configuration for authorization requirements

Since tests are generated automatically, no maintenance is necessary, other than to rerun the generator when changing our OpenAPI specification.

This is especially handy for APIs with many endpoints. And since it includes fuzzing, it generates tests we’d never consider in the first place.

2.1. Installing CATS

We have a few installation options. The simplest two are downloading and running the JAR or binary. We’ll choose the binary option since it doesn’t require an environment with Java installed and configured, making it easier to run tests from anywhere.

After downloading it, we must add the cats binary to our environment path to run it from anywhere.

2.2. Running Tests

We need to specify at least two arguments to run cats: contract and server. In our case, the OpenAPI specification URL is at /api-docs:

$ cats --contract=http://localhost:8080/api-docs --server=http://localhost:8080

We could also pass the contract as a JSON or YAML local file containing the specification.

Let’s check an example where this file is in the same directory where we’re running CATS:

$ cats --contract=api-docs.yml --server=http://localhost:8080

By default, CATS will run tests on all paths in the specification, but it’s also possible to restrict it to only a few with pattern matching:

$ cats --server=http://localhost:8080 --paths="/path/a*,/path/b"

This parameter would be helpful if we focused on a few paths per time in an extensive specification.

2.3. Including Authorization Headers

Usually, our API is secured by some form of authentication. In this case, we can include an authorization header in the command. Let’s check what it looks like when using Bearer authentication:

$ cats --server=http://localhost:8080 -H "Authorization=Bearer a-valid-token"

2.4. Report Generation

After running, it creates an HTML report locally:

CATS report

Later, we’ll review some of the errors to see how to refactor our code.

3. Project Setup

To showcase CATS, we’ll start with a simple REST CRUD API with @RestController and Bearer authentication. It’s essential to include @ApiResponse annotations, as these include important details in our OpenAPI definition, which CATS uses, like the media type and expected status code for unauthorized requests:

@RestController
@RequestMapping("/api/item")
@ApiResponse(responseCode = "401", description = "Unauthorized", content = { 
  @Content(mediaType = MediaType.TEXT_PLAIN_VALUE, schema =
    @Schema(implementation = String.class)
  ) 
})
public class ItemController {

    private ItemService service;

    // endpoints ...
}

Our request mappings have the minimum number of Swagger annotations defined, relying on defaults where possible:

@PostMapping
@ApiResponse(responseCode = "200", description = "Success", content = {
  @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, schema =
    @Schema(implementation = Item.class)
  )
})
public ResponseEntity<Item> post(@RequestBody Item item) {
    service.insert(item);
    return ResponseEntity.ok(item);
}

// GET and DELETE endpoints ...

For our payload class, we’ll include a few basic properties:

public class Item {

    private String id;
    private String name;
    private int value;

    // default getters and setters...
}

4. Analyzing Common Errors in the Report

Let’s analyze some of the errors that we got in the report so we can tackle them. Multiple similar tests are usually done for each field, so we’ll only show the detailed page for one of each.

There’s an OWASP-recommended set of security headers. The detailed test page in the report shows the ones we should include by default:

CATS security headers

Spring Security includes all these headers by default, so let’s include spring-boot-starter-security in our project:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
    <version>3.3.2</version>
</dependency>

There’s no need for a specific configuration in our SecurityFilterChain to include the security headers, so we’ll define a simple configuration with JWTs so we can pass a valid token when running cats:

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        return http
          .oauth2ResourceServer(rs -> rs.jwt(jwt -> jwt.decoder(jwtDecoder())))
          .build();
    }
}

Implementing the jwtDecoder() method depends on our needs. We can use any other authentication method that uses the authorization header.

4.2. Send Very Large Values or Values Outside the Boundary in Request Fields

When our fields have a max length specified, CATS sends a larger value and expects the server to reject these requests with a 4XX status. The max length falls back to ten thousand when not specified:

CATS test

Similarly, it sends requests with huge values and the same expectation:

CATS test

Let’s start by customizing the ObjectMapper used in our application to address these issues.

The JsonFactoryBuilder contains a StreamReadConstraints configuration we can use to set some constraints, including a max length for Strings. Let’s define a max length of 100:

@Configuration
public class JacksonConfig {

    @Bean
    public ObjectMapper objectMapper() {
        JsonFactory factory = new JsonFactoryBuilder()
          .streamReadConstraints(
            StreamReadConstraints.builder()
            .maxStringLength(100)
            .build()
          ).build();

        return new ObjectMapper(factory);
    }
}

Of course, this max length will vary depending on the requirements of our application. Most importantly, while this impedes our application from receiving oversized requests, it won’t define the constraints in our API specification.

To do that, we can include some validation annotations in our payload class:

@Size(min = 37, max = 37)
private String id;

@NotNull
@Size(min = 1, max = 20)
private String name;

@Min(1)
@Max(100)
@NotNull
private int value;

Again, the values here will depend on our requirements, but including these boundaries helps define how CATS generates tests. Finally, to reject invalid requests, we’ll modify our POST method to use the @Valid annotation:

ResponseEntity<Item> post(@Valid @RequestBody Item item) { 
    //... 
}

4.3. Malformed JSON and Dummy Requests

By default, Jackson is very lenient with requests, even accepting some malformed JSON:

CATS test

To prevent that, let’s go back to our JacksonConfig and enable an option to fail on trailing tokens:

mapper.enable(DeserializationFeature.FAIL_ON_TRAILING_TOKENS);

It’ll also accept requests that mix fields that aren’t in our Item class with fields that are, as well as dummy requests and empty JSON bodies. We can get rid of those by enforcing deserialization to fail on unknown properties:

mapper.enable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);

4.4. Decimal Numbers in Integers

Jackson will truncate decimal values to fit when we have an int property:

CATS test

For example, a value of 0.34 truncates to zero. To avoid this, let’s turn off this feature:

mapper.disable(DeserializationFeature.ACCEPT_FLOAT_AS_INT);

4.5. Zero-Width Characters in Values

Some fuzzers include zero-width characters for field names and values:

CATS test

We already enabled FAIL_ON_UNKNOWN_PROPERTIES, so we need to include some sanitization and strip zero-width characters for field values. Let’s use a custom JSON deserializer for this, starting with a utility class that defines a regex pattern for some zero-width characters:

public class RegexUtils {

    private static final Pattern ZERO_WIDTH_PATTERN = 
      Pattern.compile("[\u200B\u200C\u200D\u200F\u202B\u200E\uFEFF]");

    public static String removeZeroWidthChars(String value) {
        return value == null ? null
          : ZERO_WIDTH_PATTERN.matcher(value).replaceAll("");
    }
}

First, we use it in a custom deserializer to take care of String fields:

public class ZeroWidthStringDeserializer extends JsonDeserializer<String> {

    @Override
    public String deserialize(JsonParser parser, DeserializationContext context)
      throws IOException {
        return RegexUtils.removeZeroWidthChars(parser.getText());
    }
}

Then, we create another version for Integer fields:

public class ZeroWidthIntDeserializer extends JsonDeserializer<Integer> {

    @Override
    public Integer deserialize(JsonParser parser, DeserializationContext context)
      throws IOException {
        return Integer.valueOf(RegexUtils.removeZeroWidthChars(parser.getText()));
    }
}

Finally, we reference these deserializers in our Item fields with the @JsonDeserialize annotation:

@JsonDeserialize(using = ZeroWidthStringDeserializer.class)
private String id;

@JsonDeserialize(using = ZeroWidthStringDeserializer.class)
private String name;

@JsonDeserialize(using = ZeroWidthIntDeserializer.class)
private int value;

4.6. Bad Request Response and Schema

Many tests will result in a “Bad Request” after the changes we’ve made so far, so we’ll need to add an appropriate @ApiResponse annotation to our controller to avoid warnings in the report. Also, since the JSON response for bad requests is handled dynamically by Spring’s BasicErrorController, we need to create a class to serve as the schema in the annotation:

public class BadApiRequest {

    private long timestamp;
    private int status;
    private String error;
    private String path;

    // default getters and setters...
}

Now, we can include another definition in our controller:

@ApiResponse(responseCode = "400", description = "Bad Request", content = {
  @Content(
    mediaType = MediaType.APPLICATION_JSON_VALUE, 
    schema = @Schema(implementation = BadApiRequest.class)
  )
})

5. Refactor Results

When rerunning the reports, we can see our changes resulted in a reduction of more than 40% in errors:

Refator Results

Let’s revisit some of the test cases we tackled. We now include default security headers:

CATS test

Reject malformed JSON:

CATS test

And sanitize inputs:

CATS test

As a result, we have an overall safer API.

6. Useful Sub-Commands

CATS has sub-commands we can use to inspect a contract, replay tests, and more. Let’s see a couple of interesting ones.

6.1. Inspecting the API

To list all paths and operations defined in the API specification:

$ cats list --paths -c http://localhost:8080/api-docs

This command returns the results grouped by path:

2 paths and 4 operations:
◼ /api/v1/item: [POST, GET]
◼ /api/v1/item/{id}: [GET, DELETE]

6.2. Replaying Tests

During bug-fixing, a helpful command is replay, which reruns a specific test:

cats replay Test216

We can get the test number and replace it in the command by looking at the report. The detailed report for each test also includes the complete replay command so we can copy and paste it into our terminal.

7. Conclusion

In this article, we explored how to use CATS for automated OpenAPI testing, significantly reducing manual effort and improving test coverage. By applying changes such as adding security headers, enforcing input validation, and configuring strict deserialization, the number of reported errors for our example application decreased by over 40%.

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.

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

Partner – Moderne – NPI EA (tag=Refactoring)
announcement - icon

Modern Java teams move fast — but codebases don’t always keep up. Frameworks change, dependencies drift, and tech debt builds until it starts to drag on delivery. OpenRewrite was built to fix that: an open-source refactoring engine that automates repetitive code changes while keeping developer intent intact.

The monthly training series, led by the creators and maintainers of OpenRewrite at Moderne, walks through real-world migrations and modernization patterns. Whether you’re new to recipes or ready to write your own, you’ll learn practical ways to refactor safely and at scale.

If you’ve ever wished refactoring felt as natural — and as fast — as writing code, this is a good place to start.

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