Mockito基本使用指南

2年前 (2022) 程序员胖胖胖虎阿
318 0 0

个人博客传送门

What’s Mockito ?

Mockito是Java单元测试开发框架。在写测试单元时它可以Mock(模拟)开发中一些未完成的接口或者网络断开、数据库连接错误等方法调用。Mockito很强大文中有限还有很多使用方式未提及,请参考官方文档。官方文档链接在文章尾部给出。
eq:when() 多个参数或者搭配List根据不同index设置返回结果。

QuickStart

为了更好的表达使用一个Mock DAO层的场景,数据库还没能正常使用时但是又急需测试功能逻辑是否正确。

mapper---|
          UserMapper.java
mapperImpl---|
			  UserMapperImpl.java
service---|
          UserService.java      
//UserMapper.java
public interface UserMapper
{
    User selectOne(Integer id);
	
	void print();
}

public class UserMapperImpl implements UserMapper{
   @Override
   public User selectOne(Integer id) {
      return null;
   }

   @Override
   public void print() {
      System.out.println("test");
   }
}
//UserService.java UserService通过@Configuration配置由Spirng IOC管理
public class UserService
{
    @Autowired
    UserMapper userMapper;
    
    public User getOne(Integer id)
    {
       return userMapper.selectOne(id);
    }
}

第一步 添加依赖

Java 环境依赖

<dependency>
  <groupId>org.mockito</groupId>
  <artifactId>mockito-core</artifactId>
  <version>4.4.0</version>
</dependency>

SpringBoot 环境依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
</dependency>

第二步 mock对象与Stubbing

因为数据库无法正常使用的原因,但是service由依赖userMapper的selectOne方法,只能去mock一个假的userMapper。mock一个假的userMapper只需要使用注解@MockBeanSpringBoot会自动代替注入一个假的userMapper给service。具体原理是userMapper生成一个代理对象。

when()静态方法用于Stubbing(方法打桩),参数为非private 、final的方法调用。thenReturn设置被Stubbing的方法返回值。

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.IsNull.notNullValue;
import static org.mockito.Mockito.when;

@SpringBootTest
class MockitoDemoTests {

   @MockBean
   UserMapper userMapper;

   @Autowired
   UserService userService;

   @Test
   void mockUserMapper() {
    	when(userMapper.selectOne(0)).thenReturn(new User()); //这里mock的是userMapper的方法,service间接调用。
        User user = userService.getOne(0);
        assertThat(user,notNullValue()); //断言不为空
   }

}
//Tests passed:1

More

built-in runner

使用Mockito的注解需要在JUnit5构建mock环境

加上类注解@ExtendWith(MockitoExtension.class)

Mock

使用mock创建一个代理对象。代理对象的方法调用返回都是java基本类型默认返回值。

1、使用mock方法

   UserMapper userMapper= Mockito.mock(UserMapper.class); //不由Spring管理

2、使用@Mock注解 ,需要基于JUnit5环境。

   @Mock
   UserMapper userMapper; //不由Spring管理

​ 配合@InjectMocks注解可以注入到userService。

   @Mock
   UserMapper userMapper;

   @InjectMocks
   UserService userService;

3、使用Springboot的@MockBean注解,Spring会自动注入到测试环境里所需要依赖的对象。

   @MockBean
   UserMapper userMapper;

Spy

有时候需要在真实对象上mock方法,使用spy可以创建或者放入一个真实对象进行Stubbing,用spy创建的对象都是真实对象。

1、使用spy方法

   UserMapper userMapper= Mockito.Spy(Object); 

2、使用@spy注解,需要基于JUnit5环境。

   @Spy
   UserMapper userMapper=new UserMapperImpl();

​ 配合@InjectMocks注解可以注入到userService。

   @Spy
   UserMapper userMapper;

   @InjectMocks
   UserService userService;

3、使用Springboot的@SpyBean注解

   @SpyBean
   UserMapper userMapper;

When

when可以对调用方法进行代理Stubbing方法返回值、方法异常

mock selectOne(0) 方法返回值,thenReturn参数为调用方法期望返回值。

when(userMapper.selectOne(0)).thenReturn(new User());

mock selectOne(0) 方法异常,当调用selectOne(0) 时会抛出异常。

when(userMapper.selectOne(0)).thenThrow(new TimeoutException());

当一个void方法不需要执行时可以使用 doNothing().when()

import org.mockito.*;
import org.mockito.junit.jupiter.MockitoExtension;
import static org.mockito.Mockito.*;

@ExtendWith(MockitoExtension.class)
class MockitoDemoTests {

   @Spy
   UserMapper userMapper=new UserMapperImpl();

   @Test
   void mockUserMapper() {
      doNothing().when(userMapper).print();
      userMapper.print();
   }
}
//没有任何输出

Argument matchers

当使用when去Stubbing方法时可以根据被调用方法传参做不同的返回值或异常。

when(userMapper.selectOne(0)).thenReturn(new User());
when(userMapper.selectOne(1)).thenReturn(new User());
User userId0 = userService.getOne(0);
User userId1 = userService.getOne(1);
assertThat(userId0,not(equalTo(userId1))); //断言是否是一个对象
//passed:1 
//可以看到getOne(0)和getOne(1)不一致。

官方内置了很多类型的静态方法包含了很多场景。比如anyInt() 表示传参任何int类型数字都可以。

any()  //任何对象
any{基本类型}() 
@Test
void mockUserMapper() {
   User user=new User();
   when(userMapper.selectOne(anyInt())).thenReturn(user);
   User userId0 = userService.getOne(0);
   assertThat(userId0,equalTo(user));//断言是否是一个对象
}
//passed:1 
//可以看到user和userId0是一致。

需要注意的是 when(userMapper.selectOne(anyInt()))会被when(userMapper.selectOne(0))条件覆盖。
如果when里使用了官方内置类型any()方法其参数也应该使用。如果有指定参数可以使用eq()
when(xxx.xxx(any(),"text"))
when(xxx.xxx(any(),eq("text"))

Verification

验证被mock方法的动作

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.*;
import org.mockito.junit.jupiter.MockitoExtension;
import static org.mockito.Mockito.*;

@ExtendWith(MockitoExtension.class)
class MockitoDemoTests {

   @Spy
   UserMapper userMapper=new UserMapperImpl();

   @Test
   void mockUserMapper() {
      doNothing().when(userMapper).print();
      userMapper.print();
      verify(userMapper,times(1)).print();  //验证print()至少调用一次。
   }
}
//passed:1 

参考文档:

https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html

https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.testing.spring-boot-applications.mocking-beans

版权声明:程序员胖胖胖虎阿 发表于 2022年10月5日 下午10:24。
转载请注明:Mockito基本使用指南 | 胖虎的工具箱-编程导航

相关文章

暂无评论

暂无评论...