Android单元测试:Mockito框架的使用

Posted 唯鹿

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android单元测试:Mockito框架的使用相关的知识,希望对你有一定的参考价值。

在实际的单元测试中,我们测试的类之间会有或多或少的耦合,导致我们无法顺利的进行测试,这时我们就可以使用Mockito,Mockito库能够Mock(我喜欢理解为模拟)对象,替换我们原先依赖的真实对象,这样我们就可以避免外部的影响,只测试本类,得到更准确的结果。当然它的功能不仅仅只是这些,例如TDD(测试驱动开发)也是一大亮点,下面我在详细说明。

1.Mockito介绍

使用时在build文件中添加依赖。

dependencies 
   testCompile "org.mockito:mockito-core:2.11.0"

1.四种Mock方式

  • 普通方法:

public class MockitoTest 

    @Test
    public void testIsNotNull()
        Person mPerson = mock(Person.class); //<--使用mock方法

        assertNotNull(mPerson);
    

  • 注解方法:
public class MockitoAnnotationsTest 

    @Mock //<--使用@Mock注解
    Person mPerson;

    @Before
    public void setup()
        MockitoAnnotations.initMocks(this); //<--初始化
    

    @Test
    public void testIsNotNull()
        assertNotNull(mPerson);
    

  • 运行器方法:
@RunWith(MockitoJUnitRunner.class) //<--使用MockitoJUnitRunner
public class MockitoJUnitRunnerTest 

    @Mock //<--使用@Mock注解
    Person mPerson;

    @Test
    public void testIsNotNull()
        assertNotNull(mPerson);
    

  • MockitoRule方法
public class MockitoRuleTest 

    @Mock //<--使用@Mock注解
    Person mPerson;

    @Rule //<--使用@Rule
    public MockitoRule mockitoRule = MockitoJUnit.rule();

    @Test
    public void testIsNotNull()
        assertNotNull(mPerson);
    

其中后两种方法是结合JUnit框架去实现的,JUnit具体可以参看我的这篇:JUnit框架的使用。四种方法根据个人习惯使用,我个人推荐使用最后一种。

2.常用打桩方法

因为Mock出的对象中非void方法都将返回默认值,比如int方法将返回0,对象方法将返回null等,而void方法将什么都不做。“打桩”顾名思义就是将我们Mock出的对象进行操作,比如提供模拟的返回值等,给Mock打基础。

方法名方法描述
thenReturn(T value)设置要返回的值
thenThrow(Throwable… throwables)设置要抛出的异常
thenAnswer(Answer<?> answer)对结果进行拦截
doReturn(Object toBeReturned)提前设置要返回的值
doThrow(Throwable… toBeThrown)提前设置要抛出的异常
doAnswer(Answer answer)提前对结果进行拦截
doCallRealMethod()调用某一个方法的真实实现
doNothing()设置void方法什么也不做

上图是个简单的例子,代码语义很明确,执行什么方法时,然后就返回什么结果。当然如果我们不打桩,打印结果就是null0了。

doReturn("小小").when(mPerson).getName();
// 输出"小小"
System.out.println(mPerson.getName());

如果使用doReturn等打桩方法时,语义为:什么结果返回,执行什么方法时。这个两者的区别就是我们熟悉的while与do while。这类方法主要是为了应对无法使用thenReturn等方法的场景(比如方法为void),可读性来说thenReturn这类更好。

thenAnswer方法使用例子:

public String eat(String food)
        return food;

eat方法很简单,吃什么吐什么。。。我们用thenAnswer拿到了吃进去的东西,将返回结果重新进行处理。

3.常用验证方法

前面所说的都是状态测试,但是如果不关心返回结果,而是关心方法有否被正确的参数调用过,这时候就应该使用验证方法了。从概念上讲,就是和状态测试所不同的“行为测试”了。

verify(T mock)验证发生的某些行为 。

方法名方法描述
after(long millis)在给定的时间后进行验证
timeout(long millis)验证方法执行是否超时
atLeast(int minNumberOfInvocations)至少进行n次验证
atMost(int maxNumberOfInvocations)至多进行n次验证
description(String description)验证失败时输出的内容
times(int wantedNumberOfInvocations)验证调用方法的次数
never()验证交互没有发生,相当于times(0)
only()验证方法只被调用一次,相当于times(1)

使用代码如下:

 @Test
    public void testPersonVerifyAfter()
        when(mPerson.getAge()).thenReturn(18);
        //延时1s验证
        System.out.println(mPerson.getAge());
        System.out.println(System.currentTimeMillis());
        verify(mPerson, after(1000)).getAge();
        System.out.println(System.currentTimeMillis());

        verify(mPerson, atLeast(2)).getAge();
    

    @Test
    public void testPersonVerifyAtLeast()
        mPerson.getAge();
        mPerson.getAge();
        //至少验证2次
        verify(mPerson, atLeast(2)).getAge();
    

    @Test
    public void testPersonVerifyAtMost()
        mPerson.getAge();
        //至多验证2次
        verify(mPerson, atMost(2)).getAge();
    

    @Test
    public void testPersonVerifyTimes()
        mPerson.getAge();
        mPerson.getAge();
        //验证方法调用2次
        verify(mPerson, times(2)).getAge();
    

    @Test
    public void testPersonVerifyTimes()
        mPerson.getAge();
        mPerson.getAge();
        //验证方法在100ms超时前调用2次
        verify(mPerson, timeout(100).times(2)).getAge();
    

4.常用参数匹配器

方法名方法描述
anyObject()匹配任何对象
any(Class<T> type)与anyObject()一样
any()与anyObject()一样
anyBoolean()匹配任何boolean和非空Boolean
anyByte()匹配任何byte和非空Byte
anyCollection()匹配任何非空Collection
anyDouble()匹配任何double和非空Double
anyFloat()匹配任何float和非空Float
anyInt()匹配任何int和非空Integer
anyList()匹配任何非空List
anyLong()匹配任何long和非空Long
anyMap()匹配任何非空Map
anyString()匹配任何非空String
contains(String substring)参数包含给定的substring字符串
argThat(ArgumentMatcher <T> matcher)创建自定义的参数匹配模式

示例代码:

    @Test
    public void testPersonAny()
        when(mPerson.eat(any(String.class))).thenReturn("米饭");
        //或
        //when(mPerson.eat(anyString())).thenReturn("米饭");

        //输出米饭
        System.out.println(mPerson.eat("面条"));

    


    @Test
    public void testPersonContains()

        when(mPerson.eat(contains("面"))).thenReturn("面条");
        //输出面条
        System.out.println(mPerson.eat("面"));

    

    @Test
    public void testPersonArgThat()

        //自定义输入字符长度为偶数时,输出面条。
        when(mPerson.eat(argThat(new ArgumentMatcher<String>() 
            @Override
            public boolean matches(String argument) 
                return argument.length() % 2 == 0;
            
        ))).thenReturn("面条");
        //输出面条
        System.out.println(mPerson.eat("1234"));

    

5.其他方法

方法名方法描述
reset(T … mocks)重置Mock
spy(Class<T> classToSpy)实现调用真实对象的实现
inOrder(Object… mocks)验证执行顺序
@InjectMocks注解自动将模拟对象注入到被测试对象中

1.Spy的创建与mock一样,使用例子如下:

public class MockitoSpyTest 

    @Spy
    Person mPerson;

    @Rule
    public MockitoRule mockitoRule = MockitoJUnit.rule();

    @Test
    public void testIsNotNull()
        assertNotNull(mPerson);
    

    @Test
    public void testPersonSpy()
        //输出11
        System.out.print(mPerson.getAge());
    


2.inOrder使用代码及测试结果:

3.@InjectMocks: 创建一个实例,这个实例需要的参数用@Mock(或@Spy)注解创建的注入到该实例中。

public class Home 

    private Person mPerson;

    public Home(Person person) 
        mPerson = person;
    

    public String getMaster()
        return mPerson.getName();
    

2.其他

Mockito框架不支持mock匿名类、final类、static方法、private方法。而PowerMock框架解决了这些问题。关于PowerMock,下一篇会讲到。本篇所有代码已上传至Github。希望大家多多点赞支持!

3.参考

以上是关于Android单元测试:Mockito框架的使用的主要内容,如果未能解决你的问题,请参考以下文章

Android单元测试之Mockito的使用

Android单元测试系列-Mock之Mockito

Android单元测试学习总结

一文让你快速上手 Mockito 单元测试框架

一文让你快速上手 Mockito 单元测试框架

Mockito单元测试保姆级实战(一个java Mock测试框架)