如何测试含私有方法的Java方法?求create与checkUsername测试方案
测试包含私有方法的Java方法方案
一、推荐方式:通过测试Public方法覆盖私有逻辑
私有方法checkUsername的校验逻辑是create方法的内部实现,直接测试create方法既能验证整体功能,也能完全覆盖私有方法的所有分支。这种方式符合黑盒测试原则,无需关心内部实现细节,即使后续重构私有方法,测试用例依然有效。
测试依赖
使用JUnit 5 + Mockito模拟userRepository,避免依赖真实数据库:
<!-- 示例依赖,根据项目实际情况调整 --> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> <version>5.9.2</version> <scope>test</scope> </dependency> <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-core</artifactId> <version>4.11.0</version> <scope>test</scope> </dependency> <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-junit-jupiter</artifactId> <version>4.11.0</version> <scope>test</scope> </dependency>
测试用例代码
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; @ExtendWith(MockitoExtension.class) public class UserServiceTest { // 假设你的业务类是UserService @Mock private UserRepository userRepository; @InjectMocks private UserService userService; // 场景1:用户名为"dummy",触发UsernameUnavailableException @Test void create_WhenUsernameIsDummy_ThrowsUsernameUnavailableException() { User user = new User(); user.setUsername("dummy"); UsernameUnavailableException exception = assertThrows(UsernameUnavailableException.class, () -> userService.create(user)); assertTrue(exception.getMessage().contains("'dummy' is cannot be used!")); verify(userRepository, never()).save(any(User.class)); verify(userRepository, never()).existsByUsername(anyString()); } // 场景2:用户名不存在,正常保存用户 @Test void create_WhenUsernameNotExists_SavesUserSuccessfully() { User user = new User(); user.setUsername("new_user"); when(userRepository.existsByUsername("new_user")).thenReturn(false); when(userRepository.save(user)).thenReturn(user); User savedUser = userService.create(user); assertNotNull(savedUser); assertEquals("new_user", savedUser.getUsername()); verify(userRepository, times(1)).existsByUsername("new_user"); verify(userRepository, times(1)).save(user); } // 场景3:用户名已存在,触发UsernameIsInUseException @Test void create_WhenUsernameExists_ThrowsUsernameIsInUseException() { User user = new User(); user.setUsername("existing_user"); when(userRepository.existsByUsername("existing_user")).thenReturn(true); UsernameIsInUseException exception = assertThrows(UsernameIsInUseException.class, () -> userService.create(user)); assertTrue(exception.getMessage().contains("'existing_user' is being used by another user!")); verify(userRepository, never()).save(any(User.class)); verify(userRepository, times(1)).existsByUsername("existing_user"); } }
二、不推荐方式:直接测试私有方法
直接测试私有方法会破坏封装性,耦合内部实现细节,后续重构私有方法(比如改名、修改参数)时,测试用例需要同步修改,维护成本高。如果确实需要测试,可以通过反射实现:
反射测试私有方法的代码示例
import org.junit.jupiter.api.Test; import java.lang.reflect.Field; import java.lang.reflect.Method; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; public class UserServicePrivateMethodTest { @Test void checkUsername_WhenUsernameIsDummy_ThrowsUsernameUnavailableException() throws Exception { UserService userService = new UserService(); // 获取私有方法checkUsername Method checkUsernameMethod = UserService.class.getDeclaredMethod("checkUsername", String.class); checkUsernameMethod.setAccessible(true); // 解除访问限制 UsernameUnavailableException exception = assertThrows(UsernameUnavailableException.class, () -> checkUsernameMethod.invoke(userService, "dummy")); assertTrue(exception.getMessage().contains("'dummy' is cannot be used!")); } @Test void checkUsername_WhenUsernameExists_ThrowsUsernameIsInUseException() throws Exception { UserRepository mockRepo = mock(UserRepository.class); when(mockRepo.existsByUsername("existing_user")).thenReturn(true); UserService userService = new UserService(); // 反射注入mock的userRepository(假设是字段注入) Field repoField = UserService.class.getDeclaredField("userRepository"); repoField.setAccessible(true); repoField.set(userService, mockRepo); Method checkUsernameMethod = UserService.class.getDeclaredMethod("checkUsername", String.class); checkUsernameMethod.setAccessible(true); UsernameIsInUseException exception = assertThrows(UsernameIsInUseException.class, () -> checkUsernameMethod.invoke(userService, "existing_user")); assertTrue(exception.getMessage().contains("'existing_user' is being used by another user!")); } @Test void checkUsername_WhenUsernameNotExists_DoesNotThrowException() throws Exception { UserRepository mockRepo = mock(UserRepository.class); when(mockRepo.existsByUsername("new_user")).thenReturn(false); UserService userService = new UserService(); Field repoField = UserService.class.getDeclaredField("userRepository"); repoField.setAccessible(true); repoField.set(userService, mockRepo); Method checkUsernameMethod = UserService.class.getDeclaredMethod("checkUsername", String.class); checkUsernameMethod.setAccessible(true); // 执行方法,无异常抛出则测试通过 assertDoesNotThrow(() -> checkUsernameMethod.invoke(userService, "new_user")); } }
注意事项
- 反射测试需要处理
NoSuchMethodException、IllegalAccessException等异常 - 如果私有方法依赖其他字段(比如
userRepository),需要额外通过反射注入依赖 - 这种方式仅在特殊场景下使用,优先选择测试public方法的方案
内容的提问来源于stack exchange,提问作者taha furkan ünsal
相关产品推荐
相关产品推荐

