使用mockito写测试用例(二)

一、前言

在第一篇文章中(使用mockito写测试用例(一)),介绍了使用mock写一些的测试类。但是对于一些复杂的测试类,使用mockito还是有些困难的。但是为了覆盖率,某些类的某些方法又必须测试得到,这就是个问题。

二、Mockito的特点

①、mock的测试类无法调用静态方法(使用powermock解决)
②、如果想要mock多个层级的类,就比较复杂了。

三、使用技巧

1、对私有方法mock

如果私有方法中有调用其他层的方法,又需要mock,测试通过反射调用时就需要注意一些细节。
如下代码所示:

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserDAO userDAO;

    public void setUserDAO(UserDAO userDAO) {
        this.userDAO = userDAO;
    }

   private String queryUserByOther(String test) {
        System.out.println("1.进入service层, 入参:"+test);
        String testId = userDAO.getUserById(test);
        System.out.println("3.调用dao返回值:"+testId);
        return testId;
    }
  }

//测试:
@RunWith(SpringRunner.class)
@SpringBootTest
public class TestMock2ApplicationTests5 {

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
    }

    @InjectMocks
    UserServiceImpl userServiceImpl = new UserServiceImpl();

    @Mock
    private UserDAO userDAO = new UserDAOImpl();
    
    @Test
    public void testPrivate(){
        try {
            when(userDAO.getUserById("hi Micheal")).thenReturn("hello");
            Class<? extends UserServiceImpl> aClass = userServiceImpl.getClass();
            Method method = aClass.getDeclaredMethod("queryUserByOther", String.class);
            method.setAccessible(true); 
            String test = (String) method.invoke(userServiceImpl , "hi Micheal");
            Assert.assertEquals("测试结果:","hello",test);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

注意:这有一个坑,在method的invoke方法中,如果不使用userServiceImpl,而使用的是aClass.newInstance(),则会出现NPE。原因是:我已经将userServiceImpl这个实例设定为被测试的目标类,而通过mock的dao对象就会注入到该实例中。所有如果使用其他实例(aClass.newInstance()),肯定没有注入dao对象,当然无法调用dao的方法。
在这里插入图片描述

2、mock多层级的调用

在上一篇博客中知识简单介绍了mock的使用(文章开头有链接),但是在springboot中基于注解式的并没有介绍。本节将详细介绍。工作中都会遇到这么一种场景:在mock测试一个A类中,需要调用另一个B类的某些方法去执行,所以直接mock掉肯定不行。这时候就用到了spy,但是呢,B类中的这个方法又依赖于C类,但是不需要真实调用C类,所以我们需要将C类mock。代码如下:

//service层
@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserDAO userDAO;

    public void setUserDAO(UserDAO userDAO) {
        this.userDAO = userDAO;
    }

    @Override
    public String queryUserById(String id) {
        System.out.println("1.进入service层, 入参:"+id);
        String userById = userDAO.getUserById(id);
        System.out.println("4.调用dao返回值:"+userById);
        return userById;
    }
 }
 
 //DAO层
 @Data
@Service
public class UserDAOImpl implements UserDAO {

    @Autowired
    private UserDataBase userDataBase;

    @Override
    public String getUserById(String id) {
        System.out.println("2. 进入dao 层,入参为:" + id);
        String userById = userDataBase.getUserById(id);
        System.out.println("3.调用database返回结果:" + id);
        return userById;
    }
}

//测试用例
@RunWith(SpringRunner.class)
@SpringBootTest
public class TestMock2ApplicationTests4 {

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
    }

    @InjectMocks
    UserService userService = new UserServiceImpl();

    @Spy
    private UserDAOImpl userDAO = new UserDAOImpl();

    @Mock
    private UserDataBase userDataBase;
    
    @Test
    public void test5(){
    	//关键点
        userDAO.setUserDataBase(userDataBase);
        when(userDataBase.getUserById(Mockito.anyString())).thenReturn("songxl");
        String id = userService.queryUserById("songxl");
        System.out.println(id);
    }
}

测试结果:
在这里插入图片描述

总结
1)、个人认为关键点是:将DAO层的依赖接口(UserDataBase )mock时,通过set注入到UserDAO中。如果不写这行代码,则默认将UserDataBase 注入到userService中,所以后续执行时肯定会出现NPE。
2)、理论上可以解决多层调用的问题,但是需要mock、spy大量的数据,但是这就有一点违背初心了。毕竟使用mock写测试用例的目的就是:只需要关注被测试的业务代码即可,其他的mock掉。如果都测试,还不如直接联调简单粗暴。

doAnswer的使用

一般情况,我们使用verify、doReturn足以完成测试,但是有时候面对一些复杂的业务逻辑,就显得力不从心了。doAnswer可以用来判断执行的方法和方法的入参,并做一些处理等等,有一箭双雕的作用(doAnswer和thenAnswer作用一样的)。如下所示
方式一:在这里插入图片描述
方式二:在这里插入图片描述

thenThrow的使用

在写测试用例时,我们需要验证业务逻辑是否正确,即不仅测试成功的情景还要测试失败的场景。一句话:所有的场景都要覆盖到。在含有try-catch的逻辑部分,测试catch部分时就用到了thenThrow。如下代码所示:

	//mock业务逻辑
    @Override
    public String queryUserByName(String name) {
        String daoUserByName = null;
        try {
            System.out.println("1.service (by name)"+name);
            daoUserByName = userDAO.getUserByName(name);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return daoUserByName;
    }
    /**
     *  测试用例:
     *  thenThrow的使用
     *  参数:必须指定一个异常。
     */
    @Test
    public void test1() {
        RuntimeException exception = new RuntimeException("test exception");
        when(userDAO.getUserByName(Mockito.anyString())).thenThrow(exception);
        userService.queryUserByName("");
    }

运行结果:
在这里插入图片描述

四、参考博客

五、总结

本文介绍了mock私有方法的测试、在springboot中多层调用。但是对于静态方法的调用,mockito无法完成,可以通过powermock完成,待下一章介绍。

因为LZ也是在网上查阅各位大佬的资料及自己的理解写的,在开头已经贴出原文地址。若果有错误,万望指正;如果涉及侵权,请联系本人删除。

  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值