Mockito是一个Java单元测试框架,它允许开发者创建和配置模拟对象(mock objects),以便在隔离的环境中测试代码,尤其是当实际对象难以构造或其行为不确定时。下面是一些核心的Mockito API及其使用场景和代码示例。
基础API
-
创建Mock对象
@Mock
注解或Mockito.mock(Class<T> classToMock)
方法用于创建mock对象。
场景: 当你需要模拟一个类的行为,以便测试依赖于它的类或方法时。
示例:
@Mock List<String> mockedList;// 或者 List<String> mockedList = Mockito.mock(List.class);
-
定义Mock行为
Mockito.when(mockedMethodCall).thenReturn(value)
定义当特定方法被调用时应返回的值。
场景: 你想要控制模拟对象在特定调用下的响应。
示例:
Mockito.when(mockedList.get(0)).thenReturn("first");
-
验证交互
Mockito.verify(mockedObject).methodCall(arguments)
验证某个方法是否按照预期被调用了。
场景: 验证测试过程中mock对象的某个方法确实被正确调用。
示例:
mockedList.add("one"); Mockito.verify(mockedList).add("one");
-
捕获参数
- 使用
ArgumentCaptor
捕获传递给mock方法的参数,以便进一步检查。
场景: 需要检查方法调用的具体参数时。
示例:
ArgumentCaptor<String> argument = ArgumentCaptor.forClass(String.class); Mockito.verify(mockedList).add(argument.capture()); assertEquals("captured", argument.getValue());
- 使用
-
设置异常
Mockito.when(mockedMethodCall).thenThrow(exception)
用于模拟方法抛出异常。
场景: 测试代码对异常的处理逻辑。
示例:
Mockito.when(mockedList.get(999)).thenThrow(new IndexOutOfBoundsException());
具体使用场景示例
假设你有一个UserService
类,它依赖于UserRepository
来获取用户信息。你想测试UserService
的getUserById
方法,但不希望真实地查询数据库。
public class UserService {private UserRepository userRepository;public UserService(UserRepository userRepository) {this.userRepository = userRepository;}public User getUserById(int id) {return userRepository.findById(id);}
}public interface UserRepository {User findById(int id);
}
测试类使用Mockito模拟UserRepository
:
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.when;class UserServiceTest {@MockUserRepository userRepository;private UserService userService;@BeforeEachvoid setUp() {userService = new UserService(userRepository);}@Testvoid getUserById_ReturnsUser() {// 创建模拟行为User mockUser = new User(1, "John Doe");when(userRepository.findById(1)).thenReturn(mockUser);// 调用待测试方法User result = userService.getUserById(1);// 验证结果assertEquals(mockUser, result);// 验证交互Mockito.verify(userRepository).findById(1);}
}
在这个例子中,UserRepository
被模拟,其findById
方法被设定为返回一个预设的User
对象,从而允许我们专注于测试UserService
的逻辑,而不必关心数据库操作的细节。
当然,除了基本的Mock对象之外,Mockito还提供了Spy
功能,以及其他一些高级特性,以满足更复杂的测试需求。下面我将继续介绍这些内容。
Spy
Spy与Mock相似,但它会委托真实对象执行未被模拟的方法调用,只有被明确指定的方法才会被模拟。这在你需要大部分保留原有对象行为,仅修改或监控部分行为时非常有用。
API:
Mockito.spy(Object object)
创建一个Spy对象。
场景: 当你希望大部分情况下使用真实对象的行为,只在某些特定方法上进行模拟或验证时。
示例:
public class Calculator {public int add(int a, int b) {return a + b;}public int subtract(int a, int b) {return a - b;}
}@Test
void testSpy() {Calculator realCalculator = new Calculator();Calculator spyCalculator = Mockito.spy(realCalculator);// 模拟add方法的行为when(spyCalculator.add(1, 1)).thenReturn(3);// 调用真实subtract方法int result = spyCalculator.subtract(5, 2);// 验证add方法的模拟行为assertEquals(3, spyCalculator.add(1, 1));// 验证subtract方法的调用及结果assertEquals(3, result);verify(spyCalculator).subtract(5, 2);
}
其他高级API
-
DoAnswer: 提供更灵活的方式来定义模拟方法的行为,可以基于回调函数执行自定义逻辑。
示例:
doAnswer(invocation -> {Object[] args = invocation.getArguments();return args[0] + args[1]; }).when(someMock).someMethod(anyInt(), anyInt());
-
BDD风格: Mockito提供了一套行为驱动开发(Behavior-Driven Development)风格的API,使得测试代码更加可读。
示例:
given(mockedList.get(0)).willReturn("first"); then(mockedList).should().add("one");
-
MockitoJUnitRunner: 使用这个Runner可以自动初始化使用
@Mock
或@Spy
注解的字段,无需在每个测试方法中手动初始化。示例:
@RunWith(MockitoJUnitRunner.class) public class MyTestClass {@MockList<String> mockedList;// 测试方法... }
-
MockitoAnnotations.openMocks(this): 如果不使用
MockitoJUnitRunner
,可以在测试类的setUp
方法中手动打开Mockito注解。示例:
@BeforeEach public void setUp() {MockitoAnnotations.openMocks(this); }
通过结合使用这些API,Mockito能够帮助开发者编写更加高效、精确的单元测试,确保代码质量。
使用说明
具体使用说明和示例,你可以参考以下资源:
- 官方文档: 访问Mockito官方网站,查看最新的官方文档,包括API参考和用户指南。
- 在线教程: 网站如Stack Overflow上有大量关于Mockito使用的问答,涵盖了从基础到高级的各个方面。
- 技术博客: CSDN、博客园等技术社区有大量Mockito的教程和实践分享,比如通过搜索“Mockito进阶使用”、“Mockito详尽教程”等关键词。