Starter for building web, including RESTful, applications using Spring MVC with embedded Tomcat server.
—
Spring Boot provides comprehensive testing support for web applications with dedicated annotations and testing utilities for controllers, web layers, and full integration testing.
Test Spring MVC controllers with focused test slicing that loads only web layer components.
/**
* Web MVC test annotation for controller testing
*/
@WebMvcTest(UserController.class)
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
class UserControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private UserService userService;
@Test
void shouldGetUser() throws Exception {
// Test implementation
}
}
/**
* Web MVC test for all controllers
*/
@WebMvcTest
class AllControllersTest {
@Autowired
private MockMvc mockMvc;
}Usage Examples:
@WebMvcTest(ProductController.class)
class ProductControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private ProductService productService;
@Autowired
private ObjectMapper objectMapper;
@Test
void shouldCreateProduct() throws Exception {
Product product = new Product("Test Product", 29.99);
Product savedProduct = new Product(1L, "Test Product", 29.99);
when(productService.save(any(Product.class))).thenReturn(savedProduct);
mockMvc.perform(post("/api/products")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(product)))
.andExpect(status().isCreated())
.andExpect(jsonPath("$.id").value(1))
.andExpect(jsonPath("$.name").value("Test Product"))
.andExpect(jsonPath("$.price").value(29.99));
}
@Test
void shouldGetProduct() throws Exception {
Product product = new Product(1L, "Test Product", 29.99);
when(productService.findById(1L)).thenReturn(product);
mockMvc.perform(get("/api/products/1"))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andExpect(jsonPath("$.name").value("Test Product"));
}
@Test
void shouldReturnNotFoundForNonExistentProduct() throws Exception {
when(productService.findById(999L)).thenThrow(new ProductNotFoundException());
mockMvc.perform(get("/api/products/999"))
.andExpect(status().isNotFound());
}
}Comprehensive MockMvc API for testing web layer interactions.
/**
* MockMvc request builders and matchers
*/
public class MockMvcRequestBuilders {
public static MockHttpServletRequestBuilder get(String urlTemplate, Object... uriVars);
public static MockHttpServletRequestBuilder post(String urlTemplate, Object... uriVars);
public static MockHttpServletRequestBuilder put(String urlTemplate, Object... uriVars);
public static MockHttpServletRequestBuilder delete(String urlTemplate, Object... uriVars);
public static MockHttpServletRequestBuilder patch(String urlTemplate, Object... uriVars);
}
/**
* Request customization
*/
public interface MockHttpServletRequestBuilder {
MockHttpServletRequestBuilder contentType(MediaType mediaType);
MockHttpServletRequestBuilder content(String content);
MockHttpServletRequestBuilder header(String name, Object... values);
MockHttpServletRequestBuilder headers(HttpHeaders httpHeaders);
MockHttpServletRequestBuilder param(String name, String... values);
MockHttpServletRequestBuilder cookie(Cookie... cookies);
MockHttpServletRequestBuilder sessionAttr(String name, Object value);
MockHttpServletRequestBuilder principal(Principal principal);
}
/**
* Response verification
*/
public class MockMvcResultMatchers {
public static StatusResultMatchers status();
public static ContentResultMatchers content();
public static JsonPathResultMatchers jsonPath(String expression, Object... args);
public static HeaderResultMatchers header();
public static ModelResultMatchers model();
public static ViewResultMatchers view();
public static RedirectResultMatchers redirectedUrl(String expectedUrl);
}Usage Examples:
@Test
void shouldHandleFormSubmission() throws Exception {
mockMvc.perform(post("/users")
.param("name", "John Doe")
.param("email", "john@example.com")
.contentType(MediaType.APPLICATION_FORM_URLENCODED))
.andExpect(status().is3xxRedirection())
.andExpect(redirectedUrl("/users/success"));
}
@Test
void shouldHandleFileUpload() throws Exception {
MockMultipartFile file = new MockMultipartFile(
"file", "test.txt", "text/plain", "Hello, World!".getBytes());
mockMvc.perform(multipart("/api/upload")
.file(file)
.param("description", "Test file"))
.andExpect(status().isOk())
.andExpect(content().string(containsString("File uploaded")));
}
@Test
void shouldValidateRequestBody() throws Exception {
Product invalidProduct = new Product("", -10.0); // Invalid data
mockMvc.perform(post("/api/products")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(invalidProduct)))
.andExpect(status().isBadRequest())
.andExpect(jsonPath("$.name").value("Name is required"))
.andExpect(jsonPath("$.price").value("Price must be positive"));
}Full application context testing with @SpringBootTest and TestRestTemplate.
/**
* Full integration test with embedded server
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class UserIntegrationTest {
@Autowired
private TestRestTemplate restTemplate;
@LocalServerPort
private int port;
@Test
void shouldCreateAndRetrieveUser() {
// Integration test implementation
}
}
/**
* Integration test with mock web environment
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
@AutoConfigureTestDatabase
class UserRepositoryIntegrationTest {
@Autowired
private MockMvc mockMvc;
@Autowired
private UserRepository userRepository;
}Usage Examples:
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@TestPropertySource(locations = "classpath:application-test.properties")
class ProductIntegrationTest {
@Autowired
private TestRestTemplate restTemplate;
@Autowired
private ProductRepository productRepository;
@LocalServerPort
private int port;
@Test
void shouldCreateProductThroughApi() {
Product product = new Product("Integration Test Product", 39.99);
ResponseEntity<Product> response = restTemplate.postForEntity(
"/api/products", product, Product.class);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.CREATED);
assertThat(response.getBody().getId()).isNotNull();
assertThat(response.getBody().getName()).isEqualTo("Integration Test Product");
// Verify in database
Optional<Product> savedProduct = productRepository.findById(response.getBody().getId());
assertThat(savedProduct).isPresent();
}
@Test
void shouldGetProductList() {
// Seed test data
productRepository.save(new Product("Product 1", 10.0));
productRepository.save(new Product("Product 2", 20.0));
ResponseEntity<Product[]> response = restTemplate.getForEntity(
"/api/products", Product[].class);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(response.getBody()).hasSize(2);
}
}Testing with Spring Security integration and authentication.
/**
* Security testing with @WithMockUser
*/
@WebMvcTest(SecureController.class)
class SecureControllerTest {
@Autowired
private MockMvc mockMvc;
@Test
@WithMockUser(roles = "ADMIN")
void shouldAllowAdminAccess() throws Exception {
mockMvc.perform(get("/admin/users"))
.andExpect(status().isOk());
}
@Test
@WithMockUser(roles = "USER")
void shouldDenyUserAccess() throws Exception {
mockMvc.perform(get("/admin/users"))
.andExpect(status().isForbidden());
}
@Test
void shouldRequireAuthentication() throws Exception {
mockMvc.perform(get("/admin/users"))
.andExpect(status().isUnauthorized());
}
}
/**
* Custom security test user
*/
@WithMockUser(username = "testuser", roles = {"USER", "ADMIN"})
@Test
void shouldAllowCustomUser() throws Exception {
// Test with custom user
}Configure test-specific beans and properties.
/**
* Test configuration class
*/
@TestConfiguration
public class TestConfig {
@Bean
@Primary
public Clock testClock() {
return Clock.fixed(Instant.parse("2023-01-01T00:00:00Z"), ZoneOffset.UTC);
}
@Bean
@Primary
public EmailService mockEmailService() {
return Mockito.mock(EmailService.class);
}
}
/**
* Import test configuration
*/
@WebMvcTest
@Import(TestConfig.class)
class ControllerWithTestConfigTest {
// Test with custom configuration
}// MockMvc for web layer testing
public class MockMvc {
public ResultActions perform(MockHttpServletRequestBuilder requestBuilder) throws Exception;
}
// Test result actions for chaining expectations
public interface ResultActions {
ResultActions andExpect(ResultMatcher matcher) throws Exception;
ResultActions andDo(ResultHandler handler) throws Exception;
MvcResult andReturn() throws Exception;
}
// Test RestTemplate for integration testing
public class TestRestTemplate {
public <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Object... uriVariables);
public <T> ResponseEntity<T> postForEntity(String url, Object request, Class<T> responseType, Object... uriVariables);
public <T> ResponseEntity<T> exchange(String url, HttpMethod method, HttpEntity<?> requestEntity, Class<T> responseType, Object... uriVariables);
}
// Test annotations
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@ExtendWith(SpringExtension.class)
public @interface WebMvcTest {
Class<?>[] value() default {};
Class<?>[] controllers() default {};
boolean useDefaultFilters() default true;
}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@ExtendWith(SpringExtension.class)
public @interface SpringBootTest {
WebEnvironment webEnvironment() default WebEnvironment.MOCK;
enum WebEnvironment {
MOCK, RANDOM_PORT, DEFINED_PORT, NONE
}
}
// Mock annotations
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface MockBean {
}
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface SpyBean {
}
// Security testing annotation
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface WithMockUser {
String value() default "user";
String username() default "";
String[] roles() default {"USER"};
String[] authorities() default {};
}Install with Tessl CLI
npx tessl i tessl/maven-org-springframework-boot--spring-boot-starter-web