google-site-verification: google61fe8ba583a51912.html
@WebMvcTest and @DataJpaTest in Java

Introduction to Spring Boot Test Slices

Spring Boot provides test slicing annotations that allow you to test specific layers of your application in isolation. @WebMvcTest and @DataJpaTest are two essential annotations for testing web and data layers separately.

@WebMvcTest - Testing Web Layer

Basic @WebMvcTest Setup

import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.http.MediaType; import org.springframework.test.web.servlet.MockMvc; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.when; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; @WebMvcTest(UserController.class) // Focus only on web layer for UserController class UserControllerWebMvcTest { @Autowired private MockMvc mockMvc; // Injected automatically by @WebMvcTest @MockBean private UserService userService; // Mock the service layer @Test void shouldReturnUserWhenValidId() throws Exception { // Given User user = new User(1L, "john.doe", "[email protected]", "John", "Doe"); when(userService.getUserById(1L)).thenReturn(user); // When & Then mockMvc.perform(get("/api/users/1")) .andExpect(status().isOk()) .andExpect(jsonPath("$.id").value(1)) .andExpect(jsonPath("$.username").value("john.doe")) .andExpect(jsonPath("$.email").value("[email protected]")) .andExpect(jsonPath("$.firstName").value("John")) .andExpect(jsonPath("$.lastName").value("Doe")); } @Test void shouldReturn404WhenUserNotFound() throws Exception { // Given when(userService.getUserById(999L)).thenThrow(new UserNotFoundException("User not found")); // When & Then mockMvc.perform(get("/api/users/999")) .andExpect(status().isNotFound()) .andExpect(jsonPath("$.message").value("User not found")); } }

Controller Class

import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/api/users") public class UserController { private final UserService userService; public UserController(UserService userService) { this.userService = userService; } @GetMapping("/{id}") public ResponseEntity<User> getUserById(@PathVariable Long id) { User user = userService.getUserById(id); return ResponseEntity.ok(user); } @PostMapping public ResponseEntity<User> createUser(@RequestBody User user) { User createdUser = userService.createUser(user); return ResponseEntity.status(201).body(createdUser); } @PutMapping("/{id}") public ResponseEntity<User> updateUser(@PathVariable Long id, @RequestBody User user) { User updatedUser = userService.updateUser(id, user); return ResponseEntity.ok(updatedUser); } @DeleteMapping("/{id}") public ResponseEntity<Void> deleteUser(@PathVariable Long id) { userService.deleteUser(id); return ResponseEntity.noContent().build(); } @GetMapping public ResponseEntity<List<User>> getAllUsers( @RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "10") int size) { List<User> users = userService.getAllUsers(page, size); return ResponseEntity.ok(users); } }

Comprehensive @WebMvcTest Examples

import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.web.servlet.MockMvc; import java.util.Arrays; import java.util.List; import static org.hamcrest.Matchers.*; import static org.mockito.ArgumentMatchers.*; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.*; @WebMvcTest(UserController.class) class UserControllerComprehensiveTest { @Autowired private MockMvc mockMvc; @Autowired private ObjectMapper objectMapper; // Automatically available for JSON serialization @MockBean private UserService userService; private User user1; private User user2; private List<User> users; @BeforeEach void setUp() { user1 = new User(1L, "john.doe", "[email protected]", "John", "Doe"); user2 = new User(2L, "jane.smith", "[email protected]", "Jane", "Smith"); users = Arrays.asList(user1, user2); } @Nested @DisplayName("GET /api/users") class GetUsersTests { @Test @DisplayName("Should return all users with pagination") void shouldReturnAllUsersWithPagination() throws Exception { // Given when(userService.getAllUsers(0, 10)).thenReturn(users); // When & Then mockMvc.perform(get("/api/users") .param("page", "0") .param("size", "10")) .andDo(print()) // Useful for debugging .andExpect(status().isOk()) .andExpect(jsonPath("$", hasSize(2))) .andExpect(jsonPath("$[0].username").value("john.doe")) .andExpect(jsonPath("$[1].username").value("jane.smith")); } @Test @DisplayName("Should return empty list when no users") void shouldReturnEmptyListWhenNoUsers() throws Exception { // Given when(userService.getAllUsers(0, 10)).thenReturn(List.of()); // When & Then mockMvc.perform(get("/api/users")) .andExpect(status().isOk()) .andExpect(jsonPath("$", empty())); } } @Nested @DisplayName("POST /api/users") class CreateUserTests { @Test @DisplayName("Should create user with valid data") void shouldCreateUserWithValidData() throws Exception { // Given User newUser = new User(null, "new.user", "[email protected]", "New", "User"); User createdUser = new User(3L, "new.user", "[email protected]", "New", "User"); when(userService.createUser(any(User.class))).thenReturn(createdUser); // When & Then mockMvc.perform(post("/api/users") .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(newUser))) .andExpect(status().isCreated()) .andExpect(jsonPath("$.id").value(3)) .andExpect(jsonPath("$.username").value("new.user")) .andExpect(header().string("Location", "/api/users/3")); } @Test @DisplayName("Should return 400 for invalid user data") void shouldReturn400ForInvalidUserData() throws Exception { // Given - User with invalid email User invalidUser = new User(null, "user", "invalid-email", "First", "Last"); // When & Then mockMvc.perform(post("/api/users") .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(invalidUser))) .andExpect(status().isBadRequest()); } @Test @DisplayName("Should return 409 for duplicate username") void shouldReturn409ForDuplicateUsername() throws Exception { // Given User duplicateUser = new User(null, "john.doe", "[email protected]", "New", "User"); when(userService.createUser(any(User.class))) .thenThrow(new DuplicateUsernameException("Username already exists")); // When & Then mockMvc.perform(post("/api/users") .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(duplicateUser))) .andExpect(status().isConflict()) .andExpect(jsonPath("$.message").value("Username already exists")); } } @Nested @DisplayName("PUT /api/users/{id}") class UpdateUserTests { @Test @DisplayName("Should update user successfully") void shouldUpdateUserSuccessfully() throws Exception { // Given User updateData = new User(null, "john.updated", "[email protected]", "John", "Updated"); User updatedUser = new User(1L, "john.updated", "[email protected]", "John", "Updated"); when(userService.updateUser(eq(1L), any(User.class))).thenReturn(updatedUser); // When & Then mockMvc.perform(put("/api/users/1") .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(updateData))) .andExpect(status().isOk()) .andExpect(jsonPath("$.lastName").value("Updated")); } } @Nested @DisplayName("DELETE /api/users/{id}") class DeleteUserTests { @Test @DisplayName("Should delete user successfully") void shouldDeleteUserSuccessfully() throws Exception { // Given doNothing().when(userService).deleteUser(1L); // When & Then mockMvc.perform(delete("/api/users/1")) .andExpect(status().isNoContent()); } @Test @DisplayName("Should return 404 when deleting non-existent user") void shouldReturn404WhenDeletingNonExistentUser() throws Exception { // Given doThrow(new UserNotFoundException("User not found")) .when(userService).deleteUser(999L); // When & Then mockMvc.perform(delete("/api/users/999")) .andExpect(status().isNotFound()); } } }

Testing Security with @WebMvcTest

import org.springframework.security.test.context.support.WithMockUser; import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors; @WebMvcTest(SecuredUserController.class) class SecuredUserControllerTest { @Autowired private MockMvc mockMvc; @MockBean private UserService userService; @Test @WithMockUser(roles = "USER") @DisplayName("Should return user for authenticated user") void shouldReturnUserForAuthenticatedUser() throws Exception { // Given User user = new User(1L, "john.doe", "[email protected]", "John", "Doe"); when(userService.getUserById(1L)).thenReturn(user); // When & Then mockMvc.perform(get("/api/secured/users/1")) .andExpect(status().isOk()); } @Test @DisplayName("Should return 401 for unauthenticated request") void shouldReturn401ForUnauthenticatedRequest() throws Exception { mockMvc.perform(get("/api/secured/users/1")) .andExpect(status().isUnauthorized()); } @Test @WithMockUser(roles = "USER") @DisplayName("Should return 403 for unauthorized role") void shouldReturn403ForUnauthorizedRole() throws Exception { mockMvc.perform(get("/api/secured/users/1")) .andExpect(status().isForbidden()); } @Test @WithMockUser(username = "admin", roles = "ADMIN") @DisplayName("Should allow access for admin role") void shouldAllowAccessForAdminRole() throws Exception { // Given User user = new User(1L, "john.doe", "[email protected]", "John", "Doe"); when(userService.getUserById(1L)).thenReturn(user); // When & Then mockMvc.perform(get("/api/secured/users/1")) .andExpect(status().isOk()); } @Test @WithMockUser(username = "user", roles = "USER") @DisplayName("Should create user with CSRF token") void shouldCreateUserWithCsrfToken() throws Exception { // Given User newUser = new User(null, "new.user", "[email protected]", "New", "User"); User createdUser = new User(1L, "new.user", "[email protected]", "New", "User"); when(userService.createUser(any(User.class))).thenReturn(createdUser); // When & Then mockMvc.perform(post("/api/secured/users") .with(csrf()) // Include CSRF token .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(newUser))) .andExpect(status().isCreated()); } }

@DataJpaTest - Testing Data Layer

Basic @DataJpaTest Setup

import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager; import java.util.List; import java.util.Optional; import static org.assertj.core.api.Assertions.assertThat; @DataJpaTest class UserRepositoryTest { @Autowired private TestEntityManager testEntityManager; // Alternative to EntityManager for tests @Autowired private UserRepository userRepository; @Test void shouldSaveUser() { // Given User user = new User("john.doe", "[email protected]", "John", "Doe"); // When User savedUser = userRepository.save(user); // Then assertThat(savedUser).isNotNull(); assertThat(savedUser.getId()).isNotNull(); assertThat(savedUser.getUsername()).isEqualTo("john.doe"); assertThat(savedUser.getEmail()).isEqualTo("[email protected]"); } @Test void shouldFindUserById() { // Given User user = new User("john.doe", "[email protected]", "John", "Doe"); User savedUser = testEntityManager.persistAndFlush(user); // When Optional<User> foundUser = userRepository.findById(savedUser.getId()); // Then assertThat(foundUser).isPresent(); assertThat(foundUser.get().getUsername()).isEqualTo("john.doe"); } @Test void shouldReturnEmptyWhenUserNotFound() { // When Optional<User> foundUser = userRepository.findById(999L); // Then assertThat(foundUser).isEmpty(); } @Test void shouldFindUserByUsername() { // Given User user = new User("john.doe", "[email protected]", "John", "Doe"); testEntityManager.persistAndFlush(user); // When Optional<User> foundUser = userRepository.findByUsername("john.doe"); // Then assertThat(foundUser).isPresent(); assertThat(foundUser.get().getEmail()).isEqualTo("[email protected]"); } @Test void shouldFindUsersByLastName() { // Given User user1 = new User("john.doe", "[email protected]", "John", "Doe"); User user2 = new User("jane.doe", "[email protected]", "Jane", "Doe"); User user3 = new User("bob.smith", "[email protected]", "Bob", "Smith"); testEntityManager.persist(user1); testEntityManager.persist(user2); testEntityManager.persist(user3); testEntityManager.flush(); // When List<User> doeUsers = userRepository.findByLastName("Doe"); // Then assertThat(doeUsers).hasSize(2); assertThat(doeUsers).extracting(User::getUsername) .containsExactlyInAnyOrder("john.doe", "jane.doe"); } @Test void shouldUpdateUser() { // Given User user = new User("john.doe", "[email protected]", "John", "Doe"); User savedUser = testEntityManager.persistAndFlush(user); // When savedUser.setEmail("[email protected]"); User updatedUser = userRepository.save(savedUser); // Then assertThat(updatedUser.getEmail()).isEqualTo("[email protected]"); // Verify in database User userFromDb = testEntityManager.find(User.class, savedUser.getId()); assertThat(userFromDb.getEmail()).isEqualTo("[email protected]"); } @Test void shouldDeleteUser() { // Given User user = new User("john.doe", "[email protected]", "John", "Doe"); User savedUser = testEntityManager.persistAndFlush(user); // When userRepository.delete(savedUser); testEntityManager.flush(); // Then User deletedUser = testEntityManager.find(User.class, savedUser.getId()); assertThat(deletedUser).isNull(); } }

Entity and Repository Classes

import javax.persistence.*; import javax.validation.constraints.Email; import javax.validation.constraints.NotBlank; import javax.validation.constraints.Size; @Entity @Table(name = "users") public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @NotBlank @Size(min = 3, max = 50) @Column(unique = true, nullable = false) private String username; @Email @NotBlank @Column(unique = true, nullable = false) private String email; @NotBlank @Size(max = 50) @Column(name = "first_name", nullable = false) private String firstName; @NotBlank @Size(max = 50) @Column(name = "last_name", nullable = false) private String lastName; @Column(name = "created_at") private java.time.LocalDateTime createdAt; // Constructors public User() {} public User(String username, String email, String firstName, String lastName) { this.username = username; this.email = email; this.firstName = firstName; this.lastName = lastName; this.createdAt = java.time.LocalDateTime.now(); } // Getters and Setters public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public java.time.LocalDateTime getCreatedAt() { return createdAt; } public void setCreatedAt(java.time.LocalDateTime createdAt) { this.createdAt = createdAt; } }
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import java.util.List; import java.util.Optional; public interface UserRepository extends JpaRepository<User, Long> { Optional<User> findByUsername(String username); Optional<User> findByEmail(String email); List<User> findByLastName(String lastName); List<User> findByFirstNameContainingIgnoreCase(String firstName); @Query("SELECT u FROM User u WHERE u.email LIKE %:domain") List<User> findByEmailDomain(@Param("domain") String domain); @Query("SELECT u FROM User u WHERE u.createdAt >= :startDate AND u.createdAt <= :endDate") List<User> findUsersCreatedBetween(@Param("startDate") java.time.LocalDateTime startDate, @Param("endDate") java.time.LocalDateTime endDate); boolean existsByUsername(String username); boolean existsByEmail(String email); long countByLastName(String lastName); @Query("SELECT COUNT(u) FROM User u WHERE u.lastName = :lastName") long countUsersByLastName(@Param("lastName") String lastName); }

Comprehensive @DataJpaTest Examples

import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager; import org.springframework.dao.DataIntegrityViolationException; import java.time.LocalDateTime; import java.util.List; import static org.assertj.core.api.Assertions.*; @DataJpaTest class UserRepositoryComprehensiveTest { @Autowired private TestEntityManager testEntityManager; @Autowired private UserRepository userRepository; private User user1; private User user2; @BeforeEach void setUp() { user1 = new User("john.doe", "[email protected]", "John", "Doe"); user2 = new User("jane.smith", "[email protected]", "Jane", "Smith"); } @Nested @DisplayName("CRUD Operations") class CrudOperations { @Test @DisplayName("Should persist user with all fields") void shouldPersistUserWithAllFields() { // When User savedUser = userRepository.save(user1); // Then assertThat(savedUser.getId()).isNotNull(); assertThat(savedUser.getCreatedAt()).isNotNull(); // Verify in database User userFromDb = testEntityManager.find(User.class, savedUser.getId()); assertThat(userFromDb.getUsername()).isEqualTo("john.doe"); assertThat(userFromDb.getEmail()).isEqualTo("[email protected]"); } @Test @DisplayName("Should update user successfully") void shouldUpdateUserSuccessfully() { // Given User savedUser = testEntityManager.persistFlushFind(user1); // When savedUser.setEmail("[email protected]"); User updatedUser = userRepository.save(savedUser); // Then assertThat(updatedUser.getEmail()).isEqualTo("[email protected]"); // Verify changes are persisted testEntityManager.clear(); // Clear persistence context to force database read User userFromDb = testEntityManager.find(User.class, savedUser.getId()); assertThat(userFromDb.getEmail()).isEqualTo("[email protected]"); } @Test @DisplayName("Should delete user successfully") void shouldDeleteUserSuccessfully() { // Given User savedUser = testEntityManager.persistFlushFind(user1); // When userRepository.delete(savedUser); testEntityManager.flush(); // Then User deletedUser = testEntityManager.find(User.class, savedUser.getId()); assertThat(deletedUser).isNull(); } } @Nested @DisplayName("Query Methods") class QueryMethods { @Test @DisplayName("Should find user by username") void shouldFindUserByUsername() { // Given testEntityManager.persistAndFlush(user1); // When Optional<User> foundUser = userRepository.findByUsername("john.doe"); // Then assertThat(foundUser).isPresent(); assertThat(foundUser.get().getEmail()).isEqualTo("[email protected]"); } @Test @DisplayName("Should find users by last name") void shouldFindUsersByLastName() { // Given User user3 = new User("bob.doe", "[email protected]", "Bob", "Doe"); testEntityManager.persist(user1); testEntityManager.persist(user2); testEntityManager.persist(user3); testEntityManager.flush(); // When List<User> doeUsers = userRepository.findByLastName("Doe"); // Then assertThat(doeUsers).hasSize(2); assertThat(doeUsers).extracting(User::getUsername) .containsExactlyInAnyOrder("john.doe", "bob.doe"); } @Test @DisplayName("Should find users with email domain") void shouldFindUsersWithEmailDomain() { // Given User user3 = new User("[email protected]", "[email protected]", "Bob", "Johnson"); testEntityManager.persist(user1); testEntityManager.persist(user2); testEntityManager.persist(user3); testEntityManager.flush(); // When List<User> gmailUsers = userRepository.findByEmailDomain("gmail.com"); // Then assertThat(gmailUsers).hasSize(1); assertThat(gmailUsers.get(0).getUsername()).isEqualTo("[email protected]"); } @Test @DisplayName("Should find users created in date range") void shouldFindUsersCreatedInDateRange() { // Given User user3 = new User("old.user", "[email protected]", "Old", "User"); user3.setCreatedAt(LocalDateTime.now().minusDays(10)); testEntityManager.persist(user1); testEntityManager.persist(user2); testEntityManager.persist(user3); testEntityManager.flush(); LocalDateTime startDate = LocalDateTime.now().minusDays(1); LocalDateTime endDate = LocalDateTime.now().plusDays(1); // When List<User> recentUsers = userRepository.findUsersCreatedBetween(startDate, endDate); // Then assertThat(recentUsers).hasSize(2); assertThat(recentUsers).extracting(User::getUsername) .containsExactlyInAnyOrder("john.doe", "jane.smith"); } } @Nested @DisplayName("Validation and Constraints") class ValidationAndConstraints { @Test @DisplayName("Should enforce unique username constraint") void shouldEnforceUniqueUsernameConstraint() { // Given testEntityManager.persistAndFlush(user1); // When & Then User duplicateUser = new User("john.doe", "[email protected]", "Different", "User"); assertThatThrownBy(() -> userRepository.save(duplicateUser)) .isInstanceOf(DataIntegrityViolationException.class); } @Test @DisplayName("Should enforce unique email constraint") void shouldEnforceUniqueEmailConstraint() { // Given testEntityManager.persistAndFlush(user1); // When & Then User duplicateEmailUser = new User("different.user", "[email protected]", "Different", "User"); assertThatThrownBy(() -> userRepository.save(duplicateEmailUser)) .isInstanceOf(DataIntegrityViolationException.class); } @Test @DisplayName("Should check if username exists") void shouldCheckIfUsernameExists() { // Given testEntityManager.persistAndFlush(user1); // When & Then assertThat(userRepository.existsByUsername("john.doe")).isTrue(); assertThat(userRepository.existsByUsername("nonexistent")).isFalse(); } @Test @DisplayName("Should count users by last name") void shouldCountUsersByLastName() { // Given User user3 = new User("bob.doe", "[email protected]", "Bob", "Doe"); testEntityManager.persist(user1); testEntityManager.persist(user2); testEntityManager.persist(user3); testEntityManager.flush(); // When & Then assertThat(userRepository.countByLastName("Doe")).isEqualTo(2); assertThat(userRepository.countByLastName("Smith")).isEqualTo(1); assertThat(userRepository.countByLastName("Nonexistent")).isEqualTo(0); } } @Nested @DisplayName("Pagination and Sorting") class PaginationAndSorting { @BeforeEach void setUpPaginationData() { for (int i = 1; i <= 15; i++) { User user = new User("user" + i, "user" + i + "@example.com", "User" + i, "Test"); testEntityManager.persist(user); } testEntityManager.flush(); } @Test @DisplayName("Should return paginated results") void shouldReturnPaginatedResults() { // When org.springframework.data.domain.Page<User> page1 = userRepository.findAll( org.springframework.data.domain.PageRequest.of(0, 5)); org.springframework.data.domain.Page<User> page2 = userRepository.findAll( org.springframework.data.domain.PageRequest.of(1, 5)); // Then assertThat(page1.getContent()).hasSize(5); assertThat(page2.getContent()).hasSize(5); assertThat(page1.getTotalElements()).isEqualTo(15); assertThat(page1.getTotalPages()).isEqualTo(3); } @Test @DisplayName("Should return sorted results") void shouldReturnSortedResults() { // When List<User> users = userRepository.findAll( org.springframework.data.domain.Sort.by("username").descending()); // Then assertThat(users).hasSize(15); assertThat(users.get(0).getUsername()).isEqualTo("user9"); assertThat(users.get(1).getUsername()).isEqualTo("user8"); } } }

Testing with Different Databases

import org.springframework.test.context.ActiveProfiles; @DataJpaTest @ActiveProfiles("test") // Use test profile with H2 database class UserRepositoryH2Test { // Tests using H2 in-memory database } @DataJpaTest @AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) @ActiveProfiles("integration-test") // Use real database for integration tests class UserRepositoryIntegrationTest { // Tests using real database configuration }

Advanced Configuration and Customization

Custom @WebMvcTest Configuration

import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.context.annotation.Import; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; @WebMvcTest( value = UserController.class, includeFilters = @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = SecurityConfig.class), excludeFilters = @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = CacheConfig.class) ) @Import({SecurityConfig.class, MapperConfig.class}) // Import specific configurations @WithUserDetails("testuser") // Default user for all tests class UserControllerCustomConfigTest { @Autowired private MockMvc mockMvc; @MockBean private UserService userService; @MockBean private CacheManager cacheManager; // Excluded from main app but needed for test @Test void shouldWorkWithCustomConfiguration() throws Exception { // Test implementation } }

Custom @DataJpaTest Configuration

import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.context.annotation.Import; import org.springframework.data.auditing.DateTimeProvider; import org.springframework.data.domain.AuditorAware; import java.time.LocalDateTime; import java.util.Optional; @DataJpaTest @Import({JpaConfig.class, AuditingConfig.class}) // Import additional configurations @AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) class UserRepositoryWithAuditingTest { @Autowired private UserRepository userRepository; @Autowired private TestEntityManager testEntityManager; @Test void shouldAuditCreatedAndModifiedDates() { // Given User user = new User("audit.user", "[email protected]", "Audit", "User"); // When User savedUser = userRepository.save(user); // Then assertThat(savedUser.getCreatedAt()).isNotNull(); assertThat(savedUser.getCreatedBy()).isEqualTo("system"); } } // Configuration for auditing @Configuration @EnableJpaAuditing(auditorAwareRef = "auditorProvider", dateTimeProviderRef = "dateTimeProvider") class AuditingConfig { @Bean public AuditorAware<String> auditorProvider() { return () -> Optional.of("system"); } @Bean public DateTimeProvider dateTimeProvider() { return () -> Optional.of(LocalDateTime.now()); } }

Best Practices

1. Test Organization

// Good practice: Use nested tests for better organization @WebMvcTest(UserController.class) class UserControllerTest { @Nested @DisplayName("GET /api/users") class GetUsers { // Tests for GET endpoint } @Nested @DisplayName("POST /api/users") class CreateUser { // Tests for POST endpoint } }

2. Test Data Management

@DataJpaTest @TestInstance(TestInstance.Lifecycle.PER_CLASS) // Share test instance between methods class UserRepositoryDataTest { @BeforeAll void setUpTestData() { // Set up test data once for all tests } @AfterEach void cleanUp() { // Clean up after each test userRepository.deleteAll(); } }

3. Assertion Best Practices

class AssertionBestPractices { @Test void goodAssertions() { // Use AssertJ for fluent assertions assertThat(result).isNotNull(); assertThat(result.getUsername()).isEqualTo("john.doe"); assertThat(result.getEmail()).contains("@"); assertThat(usersList).hasSize(3).extracting(User::getLastName).contains("Doe"); } @Test void goodMockVerification() { // Verify interactions with mocks verify(userService, times(1)).getUserById(1L); verify(userService, never()).deleteUser(any()); verifyNoMoreInteractions(userService); } }

These comprehensive examples demonstrate how to effectively use @WebMvcTest for testing web controllers in isolation and @DataJpaTest for testing JPA repositories and entities. The key benefits include faster test execution, focused testing of specific layers, and easier debugging of issues in isolated components.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper