- Lab
- Core Tech

Guided: Creating a Quotes REST API with Spring Boot 3 - Part 2 - Testing
In this lab, you will gain hands-on experience in adding unit tests to a Spring Boot application, specifically focusing on the Quotes REST API. You will explore various testing approaches, including utilizing the `@SpringBootTest` annotation, extending tests using `TestRestTemplate` and `MockMvc`, and leveraging the `MockBean` annotation to mock service requests. By the end of the lab, you will have a solid foundation for creating your own suite of unit tests, enabling you to confidently make modifications to your applications while ensuring their stability and reliability.

Path Info
Table of Contents
-
Challenge
Introduction
Welcome to Part 2 of the lab series:
Guided: Creating a Quotes REST API with Spring Boot 3.
This series of labs will guide you through some of the basic concepts for creating a RESTful API Service using Spring Boot with the Spring Web dependency.
In this lab, you will create several different types of tests that begin to help ensure that the Quotes REST API is working correctly.
A benefit of using the Spring Initializr template is the added dependency on
spring-boot-starter-test
. This dependency can be seen in thebuild.gradle
file which imports the Spring Boot Test module. This module includes the testing libraries: JUnit, Jupiter, AssertJ, Hamcrest, Mockito, and other useful libraries. Again, thanks to this Spring Boot template, there is little infrastructure that will need to be coded for testing. Simple annotations will help with testing scenarios.The tests in this lab are just an introduction to the possible variations of testing available with Spring Boot Test, and will focus on the Core Spring Boot Test library, including JUnit Jupiter and Mockito.
To get started with this lab, click the Next step > button below.
-
Challenge
Introduction to `@SpringBootTest` and `@Autowired`
Before testing the REST API features, there are few basic configurations of a Spring Boot Test class that need to be covered. As with most things with Spring Boot, annotations will be used to configure a file as a test class.
In the
BasicTestSetup.java
file, you will see an empty class ready to be configured for testing. All of the relevant imports have already been added to the top of the file.The first step to configure this class for testing is to add the
@SpringBootTest
annotation above the class declaration:@SpringBootTest public class BasicTestSetup {
The
@SpringBootTest
annotation is used to configure an application context for testing purposes. It loads the entire application context and all the beans defined in it. This makes it suitable for integration testing, where you want to test the behavior of your application in a real environment.The first test will exercise the
QuotesService
class. As with the application code, this service bean can be injected into the test class by decorating the field containing this bean with the@Autowired
annotation:@Autowired private QuotesService quotesService;
When you annotate a field or a constructor parameter with
@Autowired
, Spring looks for a bean of the same type and injects it automatically. In this case, theQuotesService
is being injected into the test class, so you can test its behavior.The class is now configured to write the first test against the
QuotesService
:@Test public void quotesServiceShouldReturn3Quotes() { var quotes = quotesService.getAllQuotes(); assertEquals(2, quotes.size()); }
JUnit Jupiter is a popular testing framework for Java applications. It provides several annotations and assertions that make it easy to write and run tests.
The
@Test
annotation marks the method as a test method that should be executed by JUnit.The
assertEquals()
assertion checks that the size of the list of all quotes returned from theQuotesService
is 2. For this example code, theQuotesService
is hardcoded to return 3 quotes so this assertion will fail.Note:
assertEquals()
is one of several assertions available in the Jupiter library. For more advanced assertion methods, thespring-boot-starter-test
has included AssertJ and Hamcrest libraries.- It is also important to recognize that this is a contrived example. In real-world scenarios, the number of records returned by a service is usually not deterministic. However, this issue will be addressed later on in this lab.
To verify that this test is currently failing, execute the following command in the Terminal window:
./gradlew test --tests BasicTestSetup
Note: The
--tests
flag was added to filter the executed tests to just this current class being worked on.At the command line, you should see that the
BUILD FAILED
. If you scroll up in the results, you will see some details as to the failure. Specifically a statement providing the path to the created HTML report. For this lab, you can see this report by opening the link {{localhost:8080}}/build/reports/tests/test/index.html.This initial page of the report will give an overview of all tests that have been run. A link to the specific failed test(s) can be seen under the Failed tests tab. Clicking on that link will display the reason this particular test failed. As expected, the test failed because it was looking for 2 quotes, but actually returned 3.
You can fix this failing test changing the assertion to expect
3
instead of2
quotes.Rerunning the test command, the build should now pass and you should see
BUILD SUCCESSFUL
.This test has started to test the
QuotesService
class directly, but it does not verify if the/api/quotes
endpoint is functioning correctly. The subsequent steps will address this concern.When ready move to the next step, click the Next step > button below.
-
Challenge
Introduction to `TestRestTemplate`
The prior test class helped to unit test the
QuotesService
, but does not test whether the REST API is responding correctly. As seen in Part 1 of this series, you could manually test the REST API by starting the server and executing requests against the embedded server.The same types of tests can be automated in a test class using the
TestRestTemplate
. TheTestRestTemplate
is a class provided by Spring Boot that makes it easy to test RESTful web services. It provides methods for sending HTTP requests and receiving HTTP responses, similar to a web browser or a REST client.In the
TestRestTemplateTests.java
file, the basics you learned in the previous step have been added. Now that you've seen how to write a basic unit test with Spring Boot it is time to write something a little more interesting that actually tests the REST API.The
TestRestTemplate
client can be injected into this test class using the@Autowired
annotation:@Autowired private TestRestTemplate testRestTemplate;
The
TestRestTemplate
provides methods for sending HTTP requests, which means that this test class will start an embedded web server to handle these requests. So that this embedded web server does not conflict with other applications running on this computer, you can configure the embedded server to use a random port. Modify the@SpringBootTest
annotation to be:@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) public class TestRestTemplateTests {
Now you can rewrite the previous test to verify that the
/api/quotes
endpoint is configured to correctly respond with the same number of quotes:@Test public void quotesEndpointShouldReturn3Quotes () { var results = this.testRestTemplate.getForObject("/api/quotes", List.class); assertEquals(2, results.size()); }
The
getForObject()
method sends an HTTP GET request to the/api/quotes
endpoint and expects the response to be aList
object.The
assertEquals()
method performs the same check against the results, still using the wrong number of results. This helps to verify that the test does actually fail when it should.Note: The relative URL
/api/quotes
can be used because the test class is using an embedded server with a random port.To verify that this test is currently failing, execute the following command in the Terminal window. Notice the change to pass in this test class:
./gradlew test --tests TestRestTemplateTests
You can see the test results report at {{localhost:8080}}/build/reports/tests/test/index.html.
Note: If you still had the report open from the prior step, but drilled into the failing test, that specific static test class page no longer exists since the above command ran for this particular test class.
Again, you can fix this failing test by changing the assertion to expect
3
instead of2
quotes. Then rerunning the test command, the build should now pass withBUILD SUCCESSFUL
.When ready move to the next step, click the Next step > button below.
-
Challenge
Introduction to `MockMVC`
In the previous step, we used the
TestRestTemplate
class to send HTTP requests and receive HTTP responses. While this is a useful approach for testing the entire stack, depending on the embedded server, it can be slow and cumbersome to set up.In this step, you'll use the
MockMvc
class. This approach allows us to test our controller logic in isolation, without starting an embedded server or sending real HTTP requests. It provides methods for sending simulated HTTP requests and receiving simulated HTTP responses, similar to a mock HTTP client. This class also provides additional features, like support for testing JSON and XML payloads, among others.In the
MockMvcTests.java
file, the required import statements have been added, andMockMvc
can be configured. One way to configure the test class to useMockMvc
is to use Spring Boot annotations. Such as the following changes to the test class:@SpringBootTest @AutoConfigureMockMvc public class MockMvcTests { @Autowired private MockMvc mockMvc;
@AutoConfigureMockMvc
tells Spring Boot to automatically configure a MockMvc instance and inject it into the test class using themockMvc
field with the@Autowired
annotation.Note: If you need more control over the configuration of the
MockMvc
instance, you could use theMockMvcBuilders
utility class.Now you can refactor the
quotesEndpointShouldReturn3Quotes
test from the previous step to use themockMvc
instance:@Test public void quotesEndpointShouldReturn3Quotes() throws Exception { this.mockMvc.perform(get("/api/quotes")) .andExpect(status().isOk()) .andExpect(jsonPath("$.length()").value(3)); }
Observe that the method signature includes
throws Exception
for error handling purposes. While theTestRestTemplate
handles potential exceptions in a suppressed manner,MockMvc
manages them directly in a different approach to test such scenarios.The
mockMvc.perform()
method accepts aRequestBuilder
object. This object is being built with theMockMvcRequestBuilders.get()
method to send a simulated HTTP GET request to the/api/quotes
endpoint.Assertions can then be made against the simulated response from this request using various
ResultMatchers
. These assertions can be chained together using either theandExpect()
orandExpectAll()
Fluent API methods.In this case, the response is first checked that the returned status code is
200 OK
. Then the expected JSON response is checked that itslength
property is equal to the expected 3 quotes.By chaining these methods together, we're able to create a single, expressive assertion that checks the length of the JSON array returned by the API. This makes the test more readable and easier to understand than if we were to use multiple lines of code to create the same assertion.
This test can be verified by running the command:
./gradlew test --tests MockMvcTests
You can see the test results report at {{localhost:8080}}/build/reports/tests/test/index.html.
Compared to the
TestRestTemplate
, this test should be quicker since it doesn't rely on an embedded server. This is particularly valid when more tests are added.This contrived test is still dependent on the
QuoteService
returning an exact number of quotes. This will be addressed in the next step.When ready move to the next step, click the Next step > button below.
-
Challenge
Introduction to `MockBean`
The previous tests have utilized the
QuotesService
's actual instance, which has functioned well so far as the service returns precisely three quotes in this contrived example. However, this assumption cannot be made for an actual production service, as the service may include other dependencies like a persistence layer. This can result in fragile tests that rely on assumptions and extra layers that should not be tested at this time.To help alleviate these issues, the
QuotesService
can be mocked to return an expected result and remove the dependency on any other layers that the service may also depend on.In the same
MockMvcTests.java
file, you can add the following code just above the test method :@MockBean private QuotesService quotesService;
When the
@MockBean
annotation is added to theQuotesService
field, a mocked version of the service will be injected. By mocking theQuotesService
, the test for the/api/quotes
endpoint can be separated from the actual implementation's dependencies, and you can regulate the data returned by theQuotesService
while testing.The
Mockito.when()
method can now be used to declare what should be returned when thegetAllQuotes()
method is called.At the top of the
quotesEndpointShouldReturn3Quotes()
test method, add the following lines to mock what is returned by thegetAllQuotes()
method:@Test public void quotesEndpointShouldReturn3Quotes() throws Exception { List<String> quotes = Arrays.asList("quote1", "quote2"); Mockito.when(quotesService.getAllQuotes()).thenReturn(quotes);
In this case, the mock will return a list of 2 quotes. You can now be certain of any assertions made about the behavior of the
/api/quotes
endpoint.Rerunning the gradlew test command for this class file:
./gradlew test --tests MockMvcTests
Unless you changed the expected results to
2
, you will notice that the build again failed. Reviewing the test results report at {{localhost:8080}}/build/reports/tests/test/index.html, you will see that it is now returning 2 quotes instead of 3.When ready to move to the next step, click the Next step > button below.
-
Challenge
Next Steps
Congratulations on completing Part 2 of this guided lab series on Spring!
You are welcome to continue working with this lab to see what improvements you can make.
Some ideas are to:
- Create a
MockMvc
test for the/api/quotes/0
endpoint that mocks thegetQuoteByIndex(int index)
method. - Create a
MockMvc
test for the POST/api/quotes
method. Note that this method returns a201 Created
status. - Look up the
Hamcrest
orAssertJ
assertion libraries to see some of the advanced assertions that could be used.
The best to you as you continue your learning journey here at Pluralsight. Hope to see you in Part 3 of this series.
- Create a
What's a lab?
Hands-on Labs are real environments created by industry experts to help you learn. These environments help you gain knowledge and experience, practice without compromising your system, test without risk, destroy without fear, and let you learn from your mistakes. Hands-on Labs: practice your skills before delivering in the real world.
Provided environment for hands-on practice
We will provide the credentials and environment necessary for you to practice right within your browser.
Guided walkthrough
Follow along with the author’s guided walkthrough and build something new in your provided environment!
Did you know?
On average, you retain 75% more of your learning if you get time for practice.