0


如何完成单元测试:Mockito+Junit 使用实战

  1. 相信大家在不少书籍、文章中都看过大佬们强调单元测试如何如何重要,一写大公司也会强调单元测试的重要性。但是如何完成单元测试却鲜有文章提及,这对一写萌新开发就相当不友好了。
  2. 这篇文章我就使用MockitoJunit这两个工具,给大家分享一下。如果有大佬发现哪里有谬误,欢迎指正(#^.^#)。

为什么要使用mockito

  1. 上图目录结构,想必大家都不陌生。如果没有单元测试。各位小伙伴,如何完成功能的验证呢?
  2. 有的小伙伴可能会想着把项目运行起来,调用Controller 对应的接口,通过debug来查看对应功能是否符合预期。
  3. 我只想说并不是所有时候,你都能在本机启动整个项目的。而且,就算你本地运行的起来这个项目,当一个功能同时由多个人共同开发时,功能不符合预期,到底是我们负责的部分的问题,还是其他伙伴负责的功能有问题呢?你打算阅读其他伙伴的功能吗?此外,当功能升级需要向下兼容时,这种方式也无法保证。
  4. 好的,现在你也意识到了,我应该只测试我编辑的内容就足够了。但是我的Service有依赖其他bean呀。没有spring帮我们注入,难道我们手动实例化吗?好的,但是依赖的Bean又依赖了其他bean, 甚至是个mapper怎么办?
  1. package org.example.service.impl;
  2. import lombok.Setter;
  3. import org.example.business.DemoBusinessOne;
  4. import org.example.business.DemoBusinessTwo;
  5. import org.example.pojo.bo.DemoBO;
  6. import org.example.service.DemoService;
  7. import org.springframework.beans.factory.annotation.Autowired;
  8. import org.springframework.stereotype.Service;
  9. import java.util.Optional;
  10. @Service
  11. public class DemoServiceImpl implements DemoService {
  12. @Setter(onMethod_ = @Autowired)
  13. private DemoBusinessOne demoBusinessOne;
  14. @Setter(onMethod_ = @Autowired)
  15. private DemoBusinessTwo demoBusinessTwo;
  16. @Override
  17. public String getDemo() {
  18. DemoBO demoBO = demoBusinessOne.doBusiness1();
  19. DemoBO demoBO1 = demoBusinessTwo.doBusiness2();
  20. return Optional.ofNullable(demoBO).map(DemoBO::getBusinessMessage).orElse("")
  21. + Optional.ofNullable(demoBO1).map(DemoBO::getBusinessMessage).orElse("");
  22. }
  23. }
  1. 这个时候mockito,就发挥作用了。先看一个简单的示例:
  1. package org.example.service.impl;
  2. import org.example.business.DemoBusinessOne;
  3. import org.example.business.DemoBusinessTwo;
  4. import org.example.pojo.bo.DemoBO;
  5. import org.junit.Test;
  6. import org.junit.runner.RunWith;
  7. import org.mockito.InjectMocks;
  8. import org.mockito.Mock;
  9. import org.mockito.Mockito;
  10. import org.mockito.junit.MockitoJUnitRunner;
  11. import static org.junit.jupiter.api.Assertions.*;
  12. // 开启 Mockito
  13. @RunWith(MockitoJUnitRunner.class)
  14. public class DemoServiceImplTest {
  15. @InjectMocks // 需要mockito 帮我们注入依赖的对象,也就是我们要测试的对象
  16. private DemoServiceImpl demoService;
  17. @Mock //依赖对象 通常我们不关心他的具体实现逻辑
  18. private DemoBusinessOne demoBusinessOne;
  19. @Mock
  20. private DemoBusinessTwo demoBusinessTwo;
  21. @Test
  22. public void getDemoTest() {
  23. // 当调用mock对象的方法时,我们让他默认返回一个对象
  24. Mockito.when(demoBusinessOne.doBusiness1()).thenReturn(new DemoBO());
  25. Mockito.when(demoBusinessTwo.doBusiness2()).thenReturn(new DemoBO());
  26. assertNotNull(demoService.getDemo());
  27. }
  28. }

导入依赖

这里我使用的junit,版本是 4.12,mockito版本为3.9.0。

  1. <dependency>
  2. <groupId>junit</groupId>
  3. <artifactId>junit</artifactId>
  4. <version>4.12</version>
  5. <scope>test</scope>
  6. </dependency>
  7. <dependency>
  8. <groupId>org.mockito</groupId>
  9. <artifactId>mockito-core</artifactId>
  10. <version>3.9.0</version>
  11. <scope>test</scope>
  12. </dependency>
  13. <dependency>
  14. <groupId>org.mockito</groupId>
  15. <artifactId>mockito-inline</artifactId>
  16. <version>3.9.0</version>
  17. <scope>test</scope>
  18. </dependency>
  19. <dependency>
  20. <groupId>org.mockito</groupId>
  21. <artifactId>mockito-junit-jupiter</artifactId>
  22. <version>3.9.0</version>
  23. <scope>test</scope>
  24. </dependency>
  25. <dependency>
  26. <groupId>org.powermock</groupId>
  27. <artifactId>powermock-api-mockito2</artifactId>
  28. <version>2.0.9</version>
  29. <scope>test</scope>
  30. </dependency>

mockito使用案例

关于spy和mock

  1. 如果你是第一次接触mockito,很有可能被一个概念搞晕(起码我刚接触的时候是这样的)——什么是spy什么是mock。简单的理解就是我们依赖的那个对象,如刚开始的那个例子中的demoBusinessOnedemoBusinessTwo,这俩的实例是由谁完成的。
  2. 如果我没有创建对象,可以使用mock,此时mockito框架会通过帮我们创建一个对象,我叫他mock对象。这个mock对象中的方法被调用时,默认返回空,当然我们可以自定义他调用时候的返回值。就像最开始的返回值。甚至可以自定义一段执行逻辑。如下:
  1. @Test
  2. public void customInstanceTest() {
  3. Mockito.when(demoBusinessOne.doBusiness1()).thenReturn(new DemoBO());
  4. Mockito.when(demoBusinessTwo.doBusiness2()).then((invocation) -> {
  5. // 如果要使用参数可以使用 invocation.getArgument(0) 获取
  6. DemoBO demoBO = new DemoBO();
  7. demoBO.setBusinessMessage("Custom Message");
  8. return demoBO;
  9. });
  10. assertEquals("Custom Message", demoService.getDemo());
  11. }
  1. 如果我已经有一个实例对象,需要用把这个实例加入到mockito框架中时,可以使用spy

这么做,可以在调用实例对象的某个方法时调用实例真实的方法,如下面代码片段所示。

  1. @InjectMocks // 需要mockito 帮我们注入依赖的对象,也就是我们要测试的对象
  2. private DemoServiceImpl demoService;
  3. // @Mock //依赖对象 通常我们不关心他的具体实现逻辑
  4. @Spy
  5. private DemoBusinessOne demoBusinessOne = new DemoBusinessTwoImpl();
  6. @Mock
  7. private DemoBusinessTwo demoBusinessTwo;
  8. @Test
  9. public void spyTest(){
  10. Mockito.when(demoBusinessOne.doBusiness1()).thenCallRealMethod();
  11. Mockito.when(demoBusinessTwo.doBusiness2()).thenReturn(new DemoBO());
  12. assertEquals("realBusiness",demoService.getDemo());
  13. }
  1. 分享一个我常用spy的场景,在测试一写业务代码时,总是很烦造一写模拟数据,利用mybatis 加上spy 就可以轻松的使用开发环境数据库中的数据了。
  2. 需要特别注意的一个点,使用spy()创建的对象时,如果想要设置调用方法时候返回的之,不要使用thenReturn。如下面代码
  1. List list = new LinkedList();
  2. List spy = spy(list);
  3. //会调用真正的方法,然后抛出数组越界异常
  4. when(spy.get(0)).thenReturn("foo");
  5. //应该这样使用
  6. doReturn("foo").when(spy).get(0);

Mockito 常用方法

  1. 这里仅介绍一些常用方法,具体使用还是得看官方文档
  • mock()
  1. //手动创建一个mock对象 可以使用 @Mock 注解替代
  2. DemoBusinessOne demoBusinessOne = Mockito.mock(DemoBusinessOne.class);
  • spy()
  1. // 窃取一个实际对象作为mock对象,可以使用 @Spy 注解替代
  2. DemoBusinessOne demoBusinessOne = Mockito.spy(new DemoBusinessTwoImpl());
  • 以下三组配合使用

    1. **定义满足什么条件:**when()
    2. ** 定义满足条件后的行为:**then()、thenReturn()、thenAnswer()、thenThrow()、thenCallRealMethod()
    3. **定义满足条件后的行为:**do()、doReturn()、doAnswer()、doThrow()、doCallRealMethod()
  • when 扩展

    1. 对于有参数的方法,我们希望根据参数进行不同的处理时,可以采用下面的方式:
  1. @Test
  2. public void argTest(){
  3. DemoBusinessThree demoBusinessThree = Mockito.mock(DemoBusinessThree.class);
  4. Mockito.when(demoBusinessThree.hasArgBusiness("test1")).thenReturn(new DemoBO());
  5. Mockito.when(demoBusinessThree.hasArgBusiness("test2")).thenReturn(null);
  6. assertNotNull(demoBusinessThree.hasArgBusiness("test1"));
  7. assertNull(demoBusinessThree.hasArgBusiness("test2"));
  8. }
  1. 此外mockito还帮我们提供了更多参数匹配规则。详情请查看ArgumentMatchers类下的方法,很简单这里不做赘述。
  • verify()

    1. verify用于验证mock对象是否完成某个行为。
  • verify 行为定义(常用)

    1. times(i) 是否执行了i
    2. atLeastOnce() 至少执行了一次
    3. atLeast(i) 至少执行了i
    4. atMostOnce() 最多执行了一次
    5. atMost(i) 最多执行了i
    6. timeout(i) 验证异步方法,如果异步方法在i内没有执行会验证失败。
    7. after(i) i时间之后验证方法时候执行

结语

  1. 《代码整洁之道》中有一句话我觉着很有道理:糟糕的单元测试不如没有测试。一个好的单元测试最起码应该满足三点:自动化、独立、可重复。当然还有一些其他大佬提出了其他原则比如FIRST,感兴趣的伙伴可以自己了解一下。
  2. 通过JunitAssert工具和Mockitoverify()的api可以,帮助我们完成自动化。通过Mock spy 可以帮助我们轻松的完成独立。
  3. 以上就是我分享的内容,当然只是一些基础的示例,想要写出优秀的单元测试,能够顺应系统的不断迭代,在我看来是一件相当需要功底的事情,如果有机会再和大家分享吧。
标签: 单元测试 junit

本文转载自: https://blog.csdn.net/qq_37977412/article/details/140514885
版权归原作者 王小溟 所有, 如有侵权,请联系我们删除。

“如何完成单元测试:Mockito+Junit 使用实战”的评论:

还没有评论