目录



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;


说明

  • 如果使用​​@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 即可。