Skip to main content

Part 2: How Automation Boosts API Quality – Spring Boot + JWT + Full Test Strategy

Note: If you haven't already, check out Part 1: Code to Confidence – Spring Boot + JWT Authentication before continuing.

Part 2 shifts focus to test automation. In this phase, you'll build a comprehensive testing pipeline that validates every change — from isolated units to full-stack security — using industry-standard tools and practices:

  • Unit tests with JUnit 5 and Mockito
  • Integration tests using Spring Boot Test and Testcontainers
  • Contract tests powered by Spring Cloud Contract and PACT
  • End-to-end (E2E) tests using RestAssured
  • Security testing

By automating these layers, you'll create a repeatable, trustworthy testing strategy that supports confident, continuous delivery of your Spring Boot API.

1. Unit Testing (Controller, Service, Repository, Model, DTO, Configs, etc.)

Unit testing focuses on verifying individual components of your application in isolation, without relying on external dependencies like databases or full Spring context.


Unit Testing the Controller Layer

Unit testing the controller layer focuses on testing individual controller methods in isolation, without involving the full Spring Boot context or actual HTTP requests. This ensures that request handling logic, input validation, and response formatting are working correctly.

AuthControllerTest.java


StudentControllerTest.java


Unit Testing the Service Layer

Unit testing the service layer focuses on verifying the business logic independently of other layers like controllers or databases. These tests ensure that your core logic behaves correctly under different scenarios, without depending on web or data layers.

AuthServiceTest.java


CustomUserDetailsServiceTest.java


StudentServiceTest.java


Unit Testing the Repository Layer

The repository layer interacts directly with the database and is responsible for data persistence and retrieval. In Spring Boot, repositories are typically interfaces that extend JpaRepository or similar Spring Data interfaces.


StudentRepositoryTest.java


UserRepositoryTest.java



Unit Testing the DTOs

DTOs (Data Transfer Objects) are simple Java objects used to transfer data between layers, often without any business logic. While they are typically considered “dumb” objects, unit testing DTOs can still be useful in certain cases.

Common scenarios where DTO unit tests are beneficial include:

  • Validation Annotations: Testing that @NotNull, @Email, @Size, etc. work as expected using a validation framework.
  • Custom Logic: If DTOs contain computed fields or methods, such logic should be tested independently.
  • Serialization/Deserialization: Ensuring that DTOs correctly map to/from JSON using tools like Jackson or Gson.

Unit Testing the Security Configs

Unit testing security configurations ensures that your authentication, authorization, and access control rules are correctly enforced in your Spring Boot application. These tests help validate that endpoints are properly secured, roles are respected, and filters behave as expected.


2. Integration Testing

This integration test validates the complete flow of the StudentController REST API using TestRestTemplate. The test runs with a real Spring Boot context (@SpringBootTest(webEnvironment = RANDOM_PORT)) and performs real HTTP calls to the application.

It begins by registering and logging in an admin user to obtain a valid JWT token. This token is then used to authenticate and perform full CRUD operations on the /students endpoint.

  • Create: Adds a new student using POST
  • Read: Fetches all students and a specific student by ID
  • Update: Modifies the student data using PUT
  • Delete: Removes the student and verifies deletion

Each step includes assertions to check HTTP status codes and response content, ensuring the backend works correctly across all layers—authentication, controller, service, and repository.



3. E2E Testing using RestAssured

This end-to-end (E2E) test uses RestAssured to simulate real client interactions with the Spring Boot application. It runs with the full application context using @SpringBootTest(webEnvironment = RANDOM_PORT) and communicates over HTTP through a randomly assigned port.

The test covers the full authentication and data flow, including:

  • User Registration: A new admin user is created using the /auth/register endpoint.
  • Login: The registered user logs in and retrieves a JWT token from the /auth/login endpoint.
  • Student Creation: A new student is created via the /students endpoint using the JWT for authorization.
  • Fetch Students: The test fetches all students to verify the new entry exists.

This E2E test ensures that the entire application—from HTTP layer to database—is functioning correctly, and that secured endpoints are working as intended under realistic usage.


4. Functional Testing (User Journey)

This test simulates a real user journey through the application, verifying core functionality from registration to resource access. Using TestRestTemplate and a full Spring Boot context, it validates the entire flow in a stateful manner.

The test performs the following steps in sequence:

  • User Registration: A new admin user is registered using the /auth/register endpoint.
  • User Login: The same user logs in to obtain a JWT token via the /auth/login endpoint.
  • Create Student: A new student is added to the system using an authorized POST request to /students.
  • Retrieve Student: The newly created student is fetched using their ID to confirm creation and correctness.

This functional test validates the user experience from start to finish, ensuring that the authentication process, protected endpoints, and student data operations work together seamlessly.

Full User Journey Test (Register → Login → Access Protected Resource)

5. Security Testing (Token)

This test ensures that protected endpoints are properly secured and only accessible with a valid JWT token. It uses MockMvc for simulating HTTP requests without starting a full server, enabling fast and isolated testing.

Two scenarios are tested:

  • Access without Token: A request to the protected /users endpoint is made without a token, expecting a 403 Forbidden response.
  • Access with Valid Token: A user is dynamically registered and logged in to generate a valid JWT. This token is then used to access the /students endpoint, expecting a successful 200 OK response.

This test verifies both positive and negative security flows, ensuring that the application:

  • Blocks unauthorized access to secured resources.
  • Allows access when a valid JWT is provided in the Authorization header.

Such testing is critical to validate the robustness of your authentication and authorization mechanisms.

6. Load Testing

Load testing helps assess how the system performs under heavy traffic and concurrent user load. Apache JMeter is used here to simulate multiple users sending requests to the Spring Boot application, measuring response times, throughput, and error rates.

Performance Testing with JMeter

We'll simulate high-load scenarios on endpoints like:

  • POST /auth/login
  • GET /students
  • POST /students
  • DELETE /students/{id}

Prerequisites:

  • JMeter installed
  • Java installed (JDK 8+)
  • Spring Boot app running on localhost:8080
  • Registered user (for secured endpoints)

Step 1: Start Your Spring Boot App

Make sure your API is running locally (on http://localhost:8080) before testing.

Step 2: Launch JMeter

  1. Go to: https://jmeter.apache.org/download_jmeter.cgi. Download the .tgz (tar.gz) file.
  2. tar -xvzf apache-jmeter-<version>.tgz
  3. mv apache-jmeter-<version> /Applications/
  4. cd /Applications/apache-jmeter-<version>/bin
  5. ./jmeter


Panch Prayag

Step 3: Create a Test Plan

Right-click Test Plan → Add → Threads (Users) → Thread Group

Set:
Number of Threads (users) = 100
Ramp-Up Period (seconds) = 10
Loop Count = 5

Step 4: Add HTTP Request Defaults

Right-click Thread Group → Add → Config Element → HTTP Request Defaults

Set:
Server Name = localhost
Port = 8080
Protocol = http

Step 5: Add HTTP Header Manager (for JWT token)

If you're testing secured endpoints:
Right-click Thread Group → Add → Config Element → HTTP Header Manager

Add header:
Name: Authorization
Value: Bearer <your-JWT-token>

Step 6: Add HTTP Sampler(s)

For each endpoint:

Example 1: GET /students
Right-click Thread Group → Add → Sampler → HTTP Request
Method: GET
Path: /students

Example 2: POST /students
Method: POST
Path: /students

Click Body Data and paste:

{
  "name": "JMeter Student",
  "email": "jmeter@mail.com",
  "course": "Performance Testing"
}

Add Content-Type: application/json in Header Manager.

Step 7: Add Listeners

To view results:

  • Right-click Thread Group → Add → Listener
  • View Results Tree
  • Summary Report
  • Aggregate Report
  • Graph Results (optional)

Step 8: Run the Test

Click Start (green triangle) → Let the test finish

Analyze metrics:

  1. Throughput (requests/sec)
  2. Average response time
  3. 90th/95th percentile latency
  4. Errors

JMeter HTML Dashboard (Recommended)

If you run JMeter in headless mode, it generates detailed graphs:

/jmeter -n -t student-management-api.jmx -l student-management-api-results.jtl -e -o ~/Desktop/student-management-api-results


Panch Prayag

7. Code Coverage with Jacoco

JaCoCo (Java Code Coverage) is a widely used, open-source tool that measures how much of your Java code is executed during test runs. It provides detailed insights to help improve test effectiveness and identify untested code paths.

When integrated with your Spring Boot project, JaCoCo generates comprehensive coverage reports that include:

  • Line Coverage: Tracks the number of lines of code executed during tests.
  • Branch Coverage: Verifies whether both true and false branches of conditionals (like if statements) are covered.
  • Method/Class Coverage: Checks which methods and classes were executed by tests.

Step 1: Add JaCoCo to Your pom.xml

<build>
  <plugins>
    <plugin>
      <groupId>org.jacoco</groupId>
      <artifactId>jacoco-maven-plugin</artifactId>
      <version>0.8.11</version>
      <executions>
        <execution>
          <goals>
            <goal>prepare-agent</goal>
          </goals>
        </execution>
        <execution>
          <id>report</id>
          <phase>verify</phase>
          <goals>
            <goal>report</goal>
          </goals>
        </execution>
      </executions>
    </plugin>
  </plugins>
</build>

Step 3: Run Tests and Generate Report

Run the following Maven command:

mvn clean verify

This will:

  • Run all your tests (unit, integration, etc.)
  • Generate a coverage report in: target/site/jacoco/index.html


Panch Prayag

Step 4: Enforce Minimum Coverage Rules (Optional)

<configuration>
  <rules>
    <rule>
      <element>BUNDLE</element>
      <limits>
        <limit>
          <counter>LINE</counter>
          <value>COVEREDRATIO</value>
          <minimum>0.80</minimum>
        </limit>
      </limits>
    </rule>
  </rules>
</configuration>

To exclude DTOs, configs, etc.:

<configuration>
  <excludes>
    <exclude>**/dto/**</exclude>
    <exclude>**/config/**</exclude>
  </excludes>
</configuration>

8. Contract Testing & Schema Validation

Contract Testing verifies that the API provider (Spring Boot backend) and the consumer (frontend/microservice) agree on:

  • What the API expects as input
  • What it returns as output
  • What the structure and status codes will be

This ensures early failure detection if someone changes the API unintentionally — even before full integration testing.

There are many tools used for Contract Testing & Schema Validation like: Spring Cloud Contract, Pact, OpenAPI + Swagger, JSON Schema Validator and RestAssured.

Use Case: Validate POST /students response contract

Consumer expects:

{
  "id": 1,
  "name": "John",
  "email": "john@mail.com",
  "course": "Math"
}

1) Option 1: Spring Cloud Contract

1. Add Dependencies

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-contract-verifier</artifactId>
</dependency>
<plugin>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-contract-maven-plugin</artifactId>
    <version>4.3.0</version>
</plugin>

2. Define Contract (Groovy or YAML)

src/test/resources/contracts/student/post_student_should_return_created.groovy

3. Run and Generate Tests

mvn clean install

2) Option 2: Pact (Consumer-Driven Contracts)

  1. Consumer (frontend) writes a pact
  2. Provider (Spring Boot) verifies it using:
<dependency>
  <groupId>au.com.dius.pact.provider</groupId>
  <artifactId>spring</artifactId>
</dependency>

More at: A Guide to Pact Contract Testing in Spring Boot Applications

Conclusion:

With this two-part journey complete, you've not only built a secure, production-ready Spring Boot REST API with JWT authentication, but also fortified it with a comprehensive suite of automated tests—spanning unit, integration, contract, end-to-end, functional, and security layers.

From manual validation via curl and Swagger UI to rigorous testing pipelines powered by RestAssured, JMeter, and JaCoCo, you're now equipped to ship code with confidence and clarity. This foundation enables faster iterations, safer deployments, and a future-proof development workflow. Happy coding—and even happier testing!

🔗 View the complete project on GitHub: student-management-api

Comments

Popular posts from this blog

Deploying Spring Boot microservices on Kubernetes Cluster

This article guides you through the deployment of two Spring Boot microservices, namely "order-service" and "inventory-service," on Kubernetes using "MiniKube" . We will establish communication between them, with "order-service" making calls to an endpoint in "inventory-service." Additionally, we will configure "order-service" to be accessible from the local machine's browser . 1) Create Spring Boot microservices The Spring Boot microservices, "order-service" and "inventory-service," have been developed and can be found in this GitHub repository. If you are interested in learning more about creating Spring Boot REST microservices, please refer to this or this (Reactive) link. 2) Build Docker Images The Docker images for both "order-service" and "inventory-service" have already been generated and deployed on DockerHub, as shown below. codeburps/order-service cod...

Circuit Breaker Pattern with Resilience4J in a Spring Boot Application

Read Also: Spring Cloud Circuit Breaker + Resilience4j Resilience4j is a lightweight fault tolerance library that draws inspiration from Netflix Hystrix but is specifically crafted for functional programming. The library offers higher-order functions, known as decorators , designed to augment any functional interface, lambda expression, or method reference with features such as Circuit Breaker, Rate Limiter, Retry, or Bulkhead . These functionalities can be seamlessly integrated within a project, class, or even applied to a single method. It's possible to layer multiple decorators on any functional interface, lambda expression, or method reference, allowing for versatile and customizable fault tolerance. While numerous annotation-based implementations exist online, this article focuses solely on the reactive approach using router predicates and router functions . How Circuit Breaker Pattern works? In general, a circuit breaker functions as an automatic electrical s...

How to create a basic Spring 6 project using Maven

Below is a step-by-step guide to creating a basic Spring project using Maven. 1) Create a Maven Project Use the following Maven command to create a new Maven project. mvn archetype:generate -DgroupId=com.tb -DartifactId=spring-demo -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false 2) Import in IntelliJ IDEA If you haven't already, open IntelliJ IDEA on your system. Go to "File" > "New" > "Project from Existing Sources..." . In the file dialog, navigate to the directory where your Maven project is located. Select the pom.xml file within the project directory and click "Open." 3) Update pom.xml In total, the application requires the below-mentioned dependencies: 4) Create Spring Configuration Create a Java configuration class that uses annotations to define your Spring beans and their dependencies. This class should be annotated with @Configuration . 5) Create the Main Application C...