美文网首页
mockito使用心得

mockito使用心得

作者: EmilioWong | 来源:发表于2018-03-10 22:13 被阅读0次

    初遇mockito

    初次看到mockito的时候,是在开发者头条读的一篇关于github上2017年开源项目使用的jar包统计的文章。mockito是排名第二还是第三忘记了(第一是junit,可见对于单元测试的重视程度及java业内对单元测试框架选择的统一)。当时引起我注意的是因为mock这个词,正好前不久了解了下mock,于是就百度了mockito,发现mockito正好可以解决api调用的模拟问题,于是当晚就试了下这个工具。

    依赖

    <dependency>
        <groupId>org.mockito</groupId>
        <artifactId>mockito-core</artifactId>
        <version>2.13.0</version>
        <scope>test</scope>
    </dependency>
    

    用法

    对要模拟返回的类使用@Mock注解。如果需要测试的类不是被模拟的类,且需要被模拟的类注入的话使用@InjectMocks注解,还要调用MockitoAnnotations.initMocks方法注入。下为例子

    @InjectMocks
    private AnalyseService analyseService;
    @Mock
    private FetchService fetchService;
    @Before
    public void setup() throws IOException {
        MockitoAnnotations.initMocks(this);
    }
    

    模拟时分为两种语法

    when(fetchService.fetchBankCard("")).thenReturn(mockBankCard);
    
    Mockito.doReturn(mockEducation).when(fetchService).fetchEducation(Mockito.any(String.class), Mockito.any(String.class));
    

    when、thenReturn、doReturn、是Mockito类里的静态方法,导包时候如果不加入static关键词的话,就需要加上Mockito. 类似Assert。

    import static org.mockito.Mockito.*;
    

    碰到的问题

    一开始一直运行不起来,各种找资料后,发现在类上加上如下注解就可以了。

    @TestExecutionListeners(listeners = {SpringBootDependencyInjectionTestExecutionListener.class, ServletTestExecutionListener.class})
    

    但是在spring boot的其他测试类里,就算没有使用mock的话,不加上面注解的情况下是无法运行的,具体原因不明。

    @RunWith(SpringRunner.class)
    @SpringBootTest
    @ContextConfiguration(classes = Application.class)
    

    例子

    @Test
    public void testMockHandleAuthMobile() throws IOException {
        final String mockStr = "{\"orderNo\":\"201709061056396790389\",\"data\":{\"result\":\"T\",\"message\":\"信息验证通过\"},\"rc\":\"0000\",\"msg\":\"查询成功\"}";
        Map mock = new ObjectMapper().readValue(mockStr, Map.class);
        when(fetchService.fetchAuthMobile(anyString(), anyString(), anyString())).thenReturn(mock);
    
        Boolean result = analyseService.handleAuthMobile("", "", "");
        assertTrue(result);
    @Test
    public void testMockFailHandleAuthMobile() throws IOException {
        final String mockStr = "{\"orderNo\":\"201709061056396790389\",\"data\":{\"result\":\"F\",\"message\":\"信息验证不通过\"},\"rc\":\"0000\",\"msg\":\"查询成功\"}";
        Map mock = new ObjectMapper().readValue(mockStr, Map.class);
        when(fetchService.fetchAuthMobile(anyString(), anyString(), anyString())).thenReturn(mock);
    
        Boolean result = analyseService.handleAuthMobile("", "", "");
        assertFalse(result);
    }
    
    @Test
    public void testMockNullHandleAuthMobile() throws IOException {
        final String mockStr = "{\"rc\":\"1000\",\"msg\":\"apikey校验失败\"}";
        Map mock = new ObjectMapper().readValue(mockStr, Map.class);
        when(fetchService.fetchAuthMobile(anyString(), anyString(), anyString())).thenReturn(mock);
    
        Boolean result = analyseService.handleAuthMobile("", "", "");
        assertNull(result);
    }
    

    如上mockStr是模拟返回对象的字符串,转换为Map后,使用when(). thenReturn()将模拟的数据返回。

    一些思考

    • 如果没有使用mock的话,我还有办法测么?
      可以。直接在源代码里把我的模拟过程写进去。也可以实现模拟效果。
    • 如果这样做的话有什么缺点?
      1.效率低,不能一次运行就把各种情况。如上面代码可以一次性模拟3中情况,如果不用mock的话,一个个模拟过去太浪费时间。
      2,无法保存测试代码,即测试代码无法复用。就算可以把测试代码注释掉保存在源代码里,也会使源代码里有很多冗余代码。
      3.真的很low
    • mockito有什么缺点没有?
      暂时没想到。语法简单,配置也还好,并不多。

    updatedAt 2018年03月23日14:17:12

    mockito干不了的事情

    今天又用了一下mockito,发现一些问题。先附上源码

    @Service
    public class UserService implements InterfaceEntityService , UserDetailsService{
    
        @Autowired
        private InitService initService;
    
        @Autowired
        private UserRepository userRepository;
    
        @Autowired
        private RoleRepository roleRepository;
    
        @Autowired
        private SmsService smsService;
    
    ……省略其他内容
    }
    

    userService是我需要的测试类,需要测试的方法是注册方法,需要用到短信校验,smsService是需要模拟的类,用于短信的发送和校验。因为我并不想要测试短信发送及校验的逻辑,我需要对smsService进行mock处理,再注入到userService。
    这时候问题来了。我只把smsService做了mock,在测试注册方法的时候需要用到roleRepository的方法,去获取角色,而roleRepository报了NPE。userService使用了@InjectMocks注解,smsService因为mock注入进去了,而initService、userRepository、roleRepository等都无法注入。查了资料后,找不到方法可以将不想要mock的类自动注入进去。最后,只能再将roleRepository mock处理一下,注入到userService,返回角色。
    此外,在查看官方文档额过程中,官方文档也给出了mockito干不了的事,这个可以解释一些思考力的第三点

    Do not mock types you don't own
    Don't mock value objects
    Don't mock everything
    Show some love with your tests

    以上是github上mockito wiki里的Remember

    在FAQ中,也写了mockito的其他一些问题

    Mockito 2.x specific limitations
    • Requires Java 6+
    • Cannot mock static methods
    • Cannot mock constructors
    • Cannot mock equals(), hashCode(). Firstly, you should not mock those methods. Secondly, Mockito defines and depends upon a specific implementation of these methods. Redefining them might break Mockito.
    • Mocking is only possible on VMs that are supported by Objenesis. Don't worry, most VMs should work just fine.
    • Spying on real methods where real implementation references outer Class via OuterClass.this is impossible. Don't worry, this is extremely rare case.
    Can I mock static methods?

    No. Mockito prefers object orientation and dependency injection over static, procedural code that is hard to understand & change. If you deal with scary legacy code you can use JMockit or Powermock to mock static methods.

    Can I mock private methods?

    No. From the standpoint of testing... private methods don't exist. More about private methods here.

    Can I verify toString()?

    No. You can stub it, though. Verification of toString() is not implemented mainly because:

    When debugging, IDE calls toString() on objects to print local variables and their content, etc. After debugging, the verification of toString() will most likely fail.
    toString() is used for logging or during string concatenation. Those invocations are usually irrelevant but they will change the outcome of verification.

    相关文章

      网友评论

          本文标题:mockito使用心得

          本文链接:https://www.haomeiwen.com/subject/hwgbfftx.html