Test Thymeleaf Controller Endpoints with Spring Boot and MockMvc

Last Updated:  August 2, 2022 | Published: August 2, 2022

Spring Boot offers excellent testing support for Spring Web MVC controllers. With the help of @WebMvcTest and MockMvc, we can verify our controller endpoints in isolation. This includes both API controllers (@RestController) as well as endpoints that return a server-side rendered view (@Controller). In this article, we're going to focus on the latter. We'll demonstrate how to test controllers that return server-side rendered views using Spring Boot, Maven, Java 17, and Thymeleaf. We'll cover HTTP GET and POST (form submission) requests and testing a secured view controller.

Thymeleaf Maven Spring Boot Project Setup

Let's start with the Maven project setup for our sample Spring Boot application using Java 17. In addition to the Spring Boot Starter Web, we include the starts for Security, Thymeleaf, and Validation:

These starters give us the baseline for developing a web application and exposing HTTP endpoints that return server-side generated Thymleaf views.

Spring Security lets us protect our endpoints with an authentication mechanism. This will become important in a later section where we explore how testing secured Thymeleaf endpoints work.

With previous Spring Boot versions, the validation part was excluded from the Spring Boot Starter Web. Hence, we have to explicitly include it if we want to use Bean Validation to verify incoming payloads or form submissions.

What's left is to include the following two testing dependencies:

The Spring Boot Starter Test aka. the swiss-army knife for testing gives us the basic testing toolbox as it transitively includes JUnit, Mockito, and other testing libraries.

The Spring Security Test dependency adds test support for protected endpoints and will make testing our secured endpoints a breeze.

Spring Boot Application Walkthrough

To understand what the upcoming Spring Boot tests with MockMvc will verify, let's quickly walk through the controller endpoints of the sample application.

We'll cover three different test scenarios. The first two test cases cover our CustomerController endpoint:

This controller exposes an HTTP GET and POST endpoint at /customers to return a customer view and to allow submitting new customers. For the sake of simplicity, we store all our customers with an in-memory list.

Designing and implementing the Thymeleaf view is not part of this article. Nevertheless, the following screenshot gives us a visual representation of what our users will see and what we're about to test:

Sample Thymeleaf View

The left part of the view includes an HTML form to create and submit new customers via the HTTP POST endpoint. On the right, we'll render all existing customers in a table.

Next, we have a second endpoint that is less complex, the AdminController:

We're going to use this endpoint to showcase how testing a secured endpoint with Thymeleaf, and Spring Boot works.

For a more realistic application setup, we'll secure our endpoints with Spring Security:

We'll allow anonymous access to our CustomerController and to our static resources (e.g., Bootstrap CSS and JS files) but require authentication for our AdminController.

Furthermore, we enable Cross-Site Request Forgery protection (CSRF) and allow users to authenticate via form login.

MockMvc Test Setup with @WebMvcTest

MockMvc is a perfect choice when testing our Thymeleaf controller endpoints in isolation. With MockMvc, we can verify our Spring Web MVC controller with a mocked servlet environment. There won't be any real HTTP communication, and we don't need to start our embedded servlet container.

This mocked environment still follows HTTP semantics, and we get fine-grained access to the resulting HTTP response, view, and model.

There are various ways to bootstrap a MockMvc environment, the easiest being the use of @WebMvcTest. With @WebMvcTest, we use a sliced Spring Boot test annotation that populates an ApplicationContext with only Spring Web MVC relevant beans for testing purposes. Furthermore, it autoconfigures a MockMvc instance that we can inject into our test.

We can even narrow down this sliced context by passing the Spring Web MVC controller class that we want to test, e.g., @WebMvcTest(CustomerController.class). Otherwise, Spring Boot scans for all our controller classes and tries to initialize them.

Thymeleaf's TemplateEngine is also part of this sliced context which allows us to verify the server-side rendering of our views. As long as we're testing our controllers with meaningful model data, we can detect a potential TemplateProcessingException quite early in the development cycle.

Furthermore, we can integrate our MockMvc environment with our Spring Security configuration. This allows us to verify our authentication and authorization rules.

When implementing the WebSecurityConfigurerAdapter class from Spring Security, @WebMvcTest will automatically detect our security configuration and protect the endpoint accordingly.

If we've already migrated to a component-based Spring Security configuration, due to the deprecation of the WebSecurityConfigurerAdapter, we have to import our security component to protect our endpoints manually. Therefore, we can include the component with @Import(NameOfSecurityConfig.class) to our test class.

Equipped with this theoretical setup knowledge, we can start writing our first test with MockMvc for our Thymeleaf controller endpoints with Spring Boot.

Test a Thymeleaf HTTP GET with MockMvc

Let's start with a test for our CustomerController HTTP GET endpoint.

As described in the previous section, we're using @WebMvcTest for this purpose and injected the autoconfigured MockMvc instance:

With MockMvc, we can chain our request and response verifications with a fluent API. We first specify the URL and HTTP method. In our example, we're performing an HTTP GET request against /customers. Remember that we're working with a mocked servlet environment; no real HTTP communication happens for this test.

As part of the following verifications (chaining andExpect), we verify the following:

  • Our Spring Boot controller endpoint returns 200, indicating that an anonymous user can access the endpoint, and Thymeleaf can render the view
  • The name of the returned view matches our expected view name customers
  • The returned model has the expected attributes. We could even verify the model's content and write assertions for it.

That's quite a lot we can verify with almost less than ten lines of total test setup code.

MockMvc makes it convenient for us to verify the response of our Spring Web MVC controller endpoint. We can even go further and verify HTTP response headers or flash attributes when redirecting the user.

Test a Thymeleaf HTTP POST Form Submission with MockMvc

Moving on to the next test, we now want to verify a form submission (HTTP POST) with MockMvc.

Once our users fill out the customer form and hit Submit, they'll initiate an HTTP POST request from the browser to our Spring Boot application.

Unlike a REST API, the payload of the form is not formatted as JSON inside the HTTP body. By default, the browser passes the form data as application/x-www-form-urlencoded. In our example, the content of a form submission looks like the following name=Duke&number=C042&email=duke%40java.org.

When testing this endpoint with Spring Boot and MockMvc, we have to mimic the browser and construct the request accordingly.

With MockMvc, we can construct the request with:

In the test example above, we specify an HTTP POST request and include our form submission attributes as parameters. As we'll redirect our users after they form submission back to /customers, we expect an HTTP status code in the 3xx range.

However, upon executing the test, we'll get an HTTP status code 403:

Even though our customer endpoint is publicly available, we forgot about CSRF. During runtime, Spring Security will inject a hidden _csrf form input value for all our forms. This helps us protect our application from CSRF attacks.

Hence, when testing and invoking our controller, we have to attach a valid token. Fortunately, Spring Security Test comes with a MockMvc integration and a RequestPostProcessor that we can attach to our MockMvc request setup:

The additional line with(csrf()) lets us now invoke our endpoint with a valid CSRF token and test the form submission.

Testing a Secured Thymeleaf View Endpoint

For our last test, we move over to the AdminController. Compared to the previous tests for the CustomerController, the AdminController is only accessible for authenticated users.

When accessing http://localhost:8080/customers in the browser, our users get redirected to a form login page. After entering valid credentials, they're allowed to access the protected page.

Let's verify the endpoint protection with a first test:

Instead of assuming an HTTP status code of 200, we're expecting a redirect to our login page.

As mentioned in one of the sections above, we have to manually include our Spring Security configuration when using a component-based configuration. That's what the @Import(SecurityConfig.class) on top of the test class is for.

For our sample application, we're not implementing our own UserDetailsService and use the default from Spring Boot. Upon application start, Spring Boot logs the generated random password to the console. This works fine for local development and building a proof of concept application.

To short circuit the authentication, we can now use functionality of the spring-security-test dependency. With the help of @WithMockUser, we can associate an authenticated user for the duration of our test:

In the background, Spring Security will place an authenticated user in the SecurityContext which lets us get past any security filter. This allows us to reach our endpoint, and we can expect an HTTP 200 status code and make further verifications for the response.

As an alternative to @WithMockUser, we can use a MockMvc PostProcessor. Spring Security Test provides a set of SecurityMockMvcRequestPostProcessors to fine-tune the authenticated user on a per-request basis:

Furthermore, this gives us the flexibility to test with different role setups to verify our authorization mechanism.

For more hands-on examples for Spring Boot and Thymeleaf, I highly recommend Wim Deblauwe's Taming Thymeleaf book.

You can find further web layer testing-related articles for Spring Boot here:

The source code for this Spring Boot Thymeleaf testing example with MockMvc is available on GitHub.

Joyful testing,

Philip

{"email":"Email address invalid","url":"Website address invalid","required":"Required field missing"}
>