java常见的mock框架
- easy-mock:已停止维护
- mockito:主流,功能强大、使用方便,推荐
依赖
如果是 springboot 项目,test 中已经包含了 mockito-core,无需额外引入
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
其它情况可以手动引入 mockito 的依赖 mockito-core
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>3.3.3</version>
<scope>test</scope>
</dependency>
mockito常用的静态方法
- Mockito 类的静态方法:when()
- ArgumentMatchers 类的静态方法:anyXxx()、any()、isA()
可以 import 直接导入这些静态方法,在代码中直接使用方法;也可以 import 导入类,代码中通过 类名.静态方法名来引用。
mock对象的创建、初始化
有2种方式创建mock对象:mock、spy
//参数指定要mock的类
UserService userService = Mockito.mock(UserService.class);
OrderService orderService = Mockito.spy(OrderService.class);
上面创建 mock 对象的方式太麻烦,通常都是使用注解
@Mock
private UserService userService;
@Spy
private OrderService orderService;
@Before
public void init() {
//初始化mock对象,使 @Mock、@Spy、@InjectMocks 注解生效
MockitoAnnotations.initMocks(this);
}
也可以使用以下注解,会自动执行 MockitoAnnotations.initMocks(this);
无需手动初始化mock对象
@MockBean
private UserService userService;
@SpyBean
private UserService userService;
如果Mock对象中要注入其它bean,也需要使用 @MockBean、@SpyBean 代替 @Mock、@Spy。
说明
- 如果使用
@RunWith(MockitoJUnitRunner.class)
,会自动初始化mock对象,可以缺省 MockitoAnnotations.initMocks(this)。但 @RunWith(MockitoJUnitRunner.class) 不能和 @RunWith(SpringRunner.class) 同时使用,而一般都需要 @RunWith(SpringRunner.class) 提供spring容器环境,所以很少用这个注解。 - 都是对应的:mock() -> @Mock -> @MockBean,spy() -> @Spy -> @SpyBean
注入其它mock对象
如果一个mock对象中需要注入其它mock对象,比如要mock UserService,UserService 依赖于另一个mock对象orderService,这时就不能直接对 UserSiervice 使用 @Mock 直接的注解,需要
- 测试类上加
@TestExecutionListeners(listeners = MockitoTestExecutionListener.class)
- 标注 @InjectMocks + @Autowired之类的注入注解
@SpringBootTest
@RunWith(SpringRunner.class)
@TestExecutionListeners(listeners = MockitoTestExecutionListener.class) //step1
public class RuleRpcServiceTest extends KdAbstractJUnit4SpringContextTests {
@Mock
private OrderService orderService;
//step2
@InjectMocks
@Resource
private UserServiceImpl userService;
@Before
public void init() {
MockitoAnnotations.initMocks(this);
}
}
mock、spy的区别
-
mock出来的对象,所有的属性、方法都会被置空,如果直接调用原本方法,会直接返回返回值类型对应的默认初始值,并不会执行方法体,通过 CallRealMethod 才能调用原方法。
-
spy出来的对象,是在目标对象|真实对象的基础上进行包装,可以直接调用原方法,不需要借助 CallRealMethod。
-
mock出来的对象可以使用 when…then… 或 do…when;
-
spy出来的对象只能使用 do…when,使用 when…then… 不会报错,但会先执行真实方法,再把 thenReturn 的 mock 数据替代原返回值进行返回,效果不对。
建议 mock 一律使用 when…then,spy 一律使用 do…when,避免混淆。
when…then…
满足指定条件时 执行指定操作
Long userId = 10L;
//thenReturn 返回指定数据
when(userService.findById(userId)).thenReturn(new User(userId, null));
//触发上面mock定义的thenReturn
User user1 = userService.findById(userId);
System.out.println(user1);
//thenCallRealMethod 调用mock类原本的方法
when(userService.findById(userId)).thenCallRealMethod();
//触发上面mock定义的thenCallRealMethod
User user2 = userService.findById(userId);
System.out.println(user2);
//thenThrow 直接抛出异常
when(userService.findById(userId)).thenThrow(new RuntimeException("xxx不合法"));
//触发上面mock定义的thenThrow
User user3 = userService.findById(userId);
do…when
注意when的写法和上一个有出入
doReturn(new User(userId, null)).when(userService).findById(userId);
doCallRealMethod().when(userService).findById(userId);
doThrow(new RuntimeException("xxx不合法")).when(userService).findById(userId);
ArgumentMatchers 参数匹配
上面的示例,在调用 userService.findById(10) 时才会触发,参数要是指定的值。
可以用 ArgumentMatchers 的静态方法限制参数类型,只要参数是指定类型即可
//any系列可以匹配指定类型的任意一个值(除了null),可以使用预定义的类型anyXxx,也可以使用自定义类型 any(Xxx.class)
when(userService.findById(anyLong())).thenReturn(new User(userId, null));
when(userService.findById(any(Long.class))).thenReturn(new User(userId, null));
//isA()和any差不多,但只校验类型、不校验值是否是null,即null也满足
when(userService.findById(isA(Long.class))).thenReturn(new User(userId, null));
一个 any | isA 对应一个参数,有多个参数时使用多个 any | isA 即可。