Testing a Spring Boot API with SpringBootTest and CircleCI
Fullstack Developer and Tech Author
When it comes to building and delivering modern web applications, the importance of continuous integration cannot be overemphasized. With the rapid pace of software development, ensuring that every change in your codebase is thoroughly tested and seamlessly integrated into your project is essential for maintaining a robust and dependable application.
In this guide, I will walk you through the process of setting up a Spring Boot API project, creating test cases with SpringBootTest, and automating the testing process with CircleCI. By the end of this tutorial, you’ll have a solid understanding of how to rigorously test your Spring Boot applications and integrate continuous integration into your development workflow for enhanced code quality and reliability. Let’s dive in!
Prerequisites
In addition to a basic understanding of Java and Spring Boot, you will need the following to get the most from this tutorial:
- A GitHub account
- A CircleCI account
- An up-to-date JDK installation
- Apache Maven installed on your system.
Cloning the demo project
Clone the sample project by running the following command.
git clone https://github.com/CIRCLECI-GWP/spring-exchange-api.git
The sample project is an exchange rate API with three endpoints:
S/N | Endpoint | Method | Function |
---|---|---|---|
1. | /currency | GET | Get all currencies supported by the API |
2. | /rates | GET | Get all the exchange rates for all the supported currencies |
3. | /rates/{currency} | GET | Get the exchange rates for a single currency |
Next, go to the root of the new folder created by the previous command (spring-exchange-api
). Set up and run the project by running the following commands.
cd spring-exchange-api
mvn -B -DskipTests clean package
mvn spring-boot:run
By default, your application will be served to port 8080. Navigate to http://127.0.0.1:8080/rates
in order to view the JSON response.
Writing tests with SpringBootTest and MockMvc
To ensure that the application works properly, the demo project already contains some test cases using @SpringBootTest
and @AutoConfigureMockMvc
to test the behavior of your API endpoints.
Navigate to the src/test/java/com/example/exchangeRates
folder, open the file named ExchangeRatesApplicationTests.java
and ensure its content is the same as the following.
package com.example.exchangeRates;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.Matchers.hasSize;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@AutoConfigureMockMvc
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class ExchangeRatesApplicationTests {
final String[] expectedCurrencies = {"EUR", "GBP", "NGN", "USD", "YEN"};
@Autowired
private MockMvc mvc;
@Autowired
private ObjectMapper objectMapper;
@Test
public void assert_correct_number_of_rates_are_returned() throws Exception {
mvc.perform(get("/rates/NGN"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.NGN").isArray())
.andExpect(jsonPath("$.NGN", hasSize(expectedCurrencies.length - 1)));
}
@Test
public void assert_400_is_returned_for_unsupported_currency() throws Exception {
mvc.perform(get("/rates/TES"))
.andExpect(status().isBadRequest());
}
@Test
public void assert_supported_currencies_are_returned_for_currency_index_route() throws Exception {
String[] expectedCurrencies = {"EUR", "GBP", "NGN", "USD", "YEN"};
MvcResult mvcResult = mvc.perform(get("/currency"))
.andExpect(status().isOk()).andReturn();
String actualResponseBody = mvcResult.getResponse().getContentAsString();
assertThat(actualResponseBody).isEqualToIgnoringWhitespace(
objectMapper.writeValueAsString(expectedCurrencies));
}
}
The test suite uses @Autowired to inject dependencies into the test class, including Spring MockMVC and an ObjectMapper
, which it uses to simulate requests to endpoints in the API.
The first test ensures that for a supported currency (NGN in this case), an HTTP_OK
response is returned. Additionally, it ensures that an array for the provided currency is returned and that the size of the array matches the expected length.
The second test ensures that for an unsupported currency, an HTTP_BAD_REQUEST
response is returned.
The final test mocks a request to get all the supported currencies. In addition to ensuring that an HTTP_OK
response is returned, it grabs the response and ensures that it matches the expected list of supported currencies.
Running the tests locally
You can run your tests with the following command.
mvn test
With a test suite, you now have a higher level of confidence that your code works as expected — a key prerequisite for an efficiently automated pipeline. With this in place, you can add your CircleCI configuration to automate everything, from checkout to testing.
Adding CircleCI configuration
In your project root directory, open the config.yml
file within the .circleci
folder and ensure its content is the same as the code below.
# Use the latest 2.1 version of CircleCI pipeline process engine.
version: 2.1
jobs:
build-and-test:
docker:
- image: cimg/openjdk:19.0.1
steps:
- checkout
- run:
name: Build
command: mvn -B -DskipTests clean package
- run:
name: Test
command: mvn test
workflows:
build-and-test:
jobs:
- build-and-test
This file holds the configuration required to automate your Spring Boot tests on CircleCI.
The first thing we do is to specify the version of CircleCI pipeline process engine. Always specify the latest version (2.1 at the time this article was written).
After specifying the CircleCI version, we specify a job named build-and-test
. This job has two key blocks. The docker
block specifies the image(s) we need for our build process to run successfully. Here we specify the CircleCI’s OpenJDK Docker image.
In the steps
block, we do three things:
- Checkout the latest code from our GitHub repository
- Build the application and download the specified dependencies
- Run the application tests
The build-and-test
job is executed as specified in the workflows
block.
Next, we need to set up a repository on GitHub and link the project to CircleCI. See this post for help pushing your project to GitHub. GitLab users can also follow along by pushing to GitLab and connecting their GitLab repo to CircleCI.
Adding the project to CircleCI
Log into your CircleCI account. If you signed up with your GitHub account, all your repositories will be displayed on your project’s dashboard.
Next to your spring-exchange-api
project. Click Set Up Project.
You will be prompted to input the name of the branch on GitHub where your configuration file is housed. Choose appropriately as shown here:
Finally, click Set Up Project to start the build. CircleCI will use your configuration file to trigger the build. Your build will start running and complete successfully.
Click build-and-test. You will see the job steps and the status of each job as shown in the screenshot below.
Now, on every change to your project, CircleCI will automatically execute the Spring Boot tests defined in your test file, allowing you to quickly add new features or fix bugs with increased confidence in the health and integrity of your application.
Conclusion
In this tutorial, I have shown you how to set up a continuous integration pipeline for automatically testing a Spring Boot application using CircleCI. Continuous integration builds on software development best practices in testing and version control to automate the process of adding new features to software. This removes the risk of human error causing downtime in the production environment. It also adds an additional level of quality control and assurance to the software being maintained. Give continuous integration a try and make code base bottlenecks a thing of the past for your team!
The entire codebase for this tutorial is available on GitHub.
Happy coding!