Here's a summary of how to set up and use @WebMvcTest with JTE and htmx for testing Spring applications, formatted for easy reference:
@WebMvcTest is a powerful Spring Boot annotation designed for "slice testing" your web layer. It focuses on testing Spring MVC controllers by auto-configuring only the components relevant to the web layer (e.g., controllers, DispatcherServlet, ViewResolvers, JSON conversion). This makes tests faster and more isolated compared to @SpringBootTest, which loads the entire application context.
To work with JTE and htmx in a Spring Boot application, ensure you have the necessary dependencies. You'd typically include:
- Spring Web: For building web applications with Spring MVC.
- JTE Template Engine: For server-side templating.
- htmx: While htmx is a client-side library, its integration impacts how your controllers respond, especially with partial HTML updates.
When you annotate a test class with @WebMvcTest, Spring Boot automatically configures a MockMvc instance for you. MockMvc allows you to perform requests against your controllers without needing a running Servlet container.
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.test.web.servlet.MockMvc;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
@WebMvcTest(YourController.class) // Specify the controller(s) to test
public class YourControllerTest {
@Autowired
private MockMvc mockMvc;
// ... your tests
}Key Points:
@WebMvcTest(YourController.class): Limits the application context to onlyYourControllerand web-related components, making tests faster and more focused. If your controller has dependencies (e.g., services, repositories), you'll need to mock them using@MockBean.MockMvc: Injected automatically, it's your primary tool for performing mock HTTP requests.
When testing controllers that render JTE views, you can use MockMvc to perform a GET request to the endpoint and then assert on the returned HTML content. MockMvc will use the configured ViewResolvers (which would include JTE's) to render the view.
@Test
void shouldReturnIndexPage() throws Exception {
mockMvc.perform(get("/"))
.andExpect(status().isOk())
.andExpect(content().contentType("text/html;charset=UTF-8")) // Or "text/html" depending on configuration
.andExpect(content().string(containsString("<h1>Welcome to JTE and HTMX App</h1>")));
}content().contentType(...): Verifies that the response is HTML.content().string(containsString(...)): Asserts that the rendered HTML contains specific text or elements.
htmx leverages specific HTTP headers and expects partial HTML responses. When testing htmx interactions with @WebMvcTest, you'll simulate these behaviors.
htmx adds a HX-Request header (value true) to indicate an htmx-initiated request. You can add this header to your MockMvc requests.
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
@Test
void shouldAddTaskViaHtmx() throws Exception {
mockMvc.perform(post("/add-task")
.param("description", "New Task")
.header("HX-Request", "true") // Simulate htmx request
.contentType(MediaType.APPLICATION_FORM_URLENCODED))
.andExpect(status().isOk())
.andExpect(content().contentType("text/html;charset=UTF-8"))
.andExpect(content().string(containsString("<li id=\"task-"))); // Expect a partial HTML snippet
// Optionally, assert on other htmx-specific response headers like HX-Trigger
// .andExpect(header().string("HX-Trigger", "taskAddedEvent"));
}header("HX-Request", "true"): Essential for mimicking htmx requests.param("description", "New Task"): Simulates form parameters.- Assertions on Partial HTML: htmx often swaps parts of the DOM, so your assertions should focus on the expected partial HTML response.
htmx allows controllers to send specific headers (e.g., HX-Trigger, HX-Redirect) to control client-side behavior. You can assert the presence and values of these headers in your tests.
@Test
void shouldRedirectAfterDelete() throws Exception {
mockMvc.perform(delete("/delete-task/123")
.header("HX-Request", "true"))
.andExpect(status().isNoContent()) // Common for successful deletions without content
.andExpect(header().string("HX-Redirect", "/tasks")); // Assert htmx redirect header
}While @WebMvcTest focuses on isolated controller testing, the provided Spring documentation highlights how MockMvc can be integrated with HtmlUnit for more comprehensive, browser-like tests. HtmlUnit provides a "headless browser" environment, allowing you to execute JavaScript and interact with the DOM as a real browser would.
If you need to test the end-to-end flow, including client-side JavaScript execution and dynamic DOM manipulation (especially if your htmx interactions involve more complex client-side logic or depend on JavaScript that modifies the DOM before or after htmx swaps), integrating HtmlUnit with MockMvc can be beneficial. However, for most controller-level tests, @WebMvcTest with MockMvc alone is sufficient and faster.
You can set up HtmlUnit with MockMvcWebClientBuilder to direct requests to your MockMvc instance instead of a real HTTP server:
// Example (not part of @WebMvcTest directly, but a complementary approach)
import com.gargoylesoftware.htmlunit.WebClient;
import org.springframework.test.web.servlet.htmlunit.MockMvcWebClientBuilder;
// ...
// In a test class, you might set up WebClient like this:
WebClient webClient = MockMvcWebClientBuilder
.mockMvcSetup(mockMvc) // assuming 'mockMvc' is autowired from @WebMvcTest
.build();
//
// Then use webClient to navigate and interact with your application as a browser.By combining the focused testing of @WebMvcTest for your controller logic and potentially using HtmlUnit for broader integration scenarios, you can thoroughly test your Spring Boot application utilizing JTE and htmx.
References: