Java新手求教:如何使用Mockito编写JUnit测试?
Mockito与JUnit入门实践指南
一、Mockito的适用场景
- 测试类依赖外部服务/复杂组件(如数据库连接、第三方API、耗时工具类)时,用Mock对象隔离依赖,避免测试时产生副作用(比如修改真实数据库数据)或难以初始化依赖。
- 验证被测试类是否正确调用了依赖方法(包括调用次数、传入参数是否符合预期)。
- 快速模拟异常场景:比如让依赖抛出指定异常,验证被测试类的异常处理逻辑。
- 无需搭建完整依赖链,就能测试核心业务逻辑,提升测试效率。
二、JUnit测试基础编写规范
以JUnit 5为例,基础流程遵循Arrange-Act-Assert(准备-执行-断言)模式:
- 测试类命名:通常为
被测试类名+Test,比如UserServiceTest。 - 测试方法:用
@Test注解标记,方法名清晰描述测试场景,比如shouldReturnUserNameWhenUserIdIsValid。 - 核心步骤:
- Arrange:初始化被测试对象、测试数据。
- Act:调用被测试类的目标方法。
- Assert:验证返回结果或状态是否符合预期。
纯JUnit测试示例:
import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; public class CalculatorTest { @Test public void shouldAddTwoNumbersCorrectly() { // Arrange Calculator calculator = new Calculator(); int num1 = 7; int num2 = 3; // Act int result = calculator.add(num1, num2); // Assert assertEquals(10, result); } }
三、Mockito核心用法
首先确保引入mockito-junit-jupiter依赖(适配JUnit 5),核心操作包括:
1. 创建Mock对象
- 手动创建:用
Mockito.mock()方法UserRepository mockRepo = Mockito.mock(UserRepository.class); - 注解自动注入:配合
@ExtendWith(MockitoExtension.class)使用@Mock注解import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; @ExtendWith(MockitoExtension.class) public class UserServiceTest { @Mock private UserRepository userRepository; // 自动生成Mock对象 private UserService userService; @BeforeEach void setUp() { userService = new UserService(userRepository); // 注入Mock依赖 } }
2. 定义Mock对象的行为(Stubbing)
用when(模拟对象.方法(参数)).thenReturn(返回值)指定方法调用的返回结果:
// 当调用userRepository.findById(1L)时,返回预设的User对象 when(userRepository.findById(1L)).thenReturn(new User(1L, "Alice"));
模拟抛出异常:
when(userRepository.findById(2L)).thenThrow(new RuntimeException("User not found"));
3. 验证方法调用
用verify(模拟对象).方法(参数)验证被测试类是否正确触发了依赖方法:
// 验证userRepository.findById(1L)被调用了一次 verify(userRepository).findById(1L); // 验证该方法从未被调用 verify(userRepository, never()).findById(3L); // 验证方法被调用了恰好2次 verify(userRepository, times(2)).save(any(User.class));
四、Mockito+JUnit完整测试示例
假设我们有如下业务类:
// 被测试类 public class UserService { private final UserRepository userRepository; public UserService(UserRepository userRepository) { this.userRepository = userRepository; } public String getUserName(Long userId) { User user = userRepository.findById(userId); if (user == null) { throw new IllegalArgumentException("Invalid user ID"); } return user.getName(); } } // 依赖接口 public interface UserRepository { User findById(Long userId); } // 数据模型 public class User { private Long id; private String name; public User(Long id, String name) { this.id = id; this.name = name; } public String getName() { return name; } }
对应的测试类:
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 { @Mock private UserRepository userRepository; @InjectMocks // 自动将@Mock对象注入到被测试类中 private UserService userService; @Test public void shouldReturnUserNameWhenUserIdExists() { // Arrange Long validUserId = 1L; User mockUser = new User(validUserId, "Charlie"); when(userRepository.findById(validUserId)).thenReturn(mockUser); // Act String userName = userService.getUserName(validUserId); // Assert assertEquals("Charlie", userName); verify(userRepository).findById(validUserId); // 验证依赖方法被正确调用 } @Test public void shouldThrowExceptionWhenUserIdIsInvalid() { // Arrange Long invalidUserId = 99L; when(userRepository.findById(invalidUserId)).thenReturn(null); // Act & Assert IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> { userService.getUserName(invalidUserId); }); assertEquals("Invalid user ID", exception.getMessage()); verify(userRepository).findById(invalidUserId); } }
五、初学者注意事项
- 不要过度Mock:如果依赖是纯逻辑工具类,直接用真实对象测试更准确,Mock只用于复杂或有副作用的依赖。
- 单一测试场景:每个测试方法只验证一个逻辑点,方法名要直观描述测试内容。
- 启用静态导入:导入
static org.mockito.Mockito.*,让测试代码更简洁。
内容的提问来源于stack exchange,提问作者User1515
相关产品推荐
相关产品推荐

