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

xiaoxiao2021-02-28  14

在实际的单元测试中,我们测试的类之间会有或多或少的耦合,导致我们无法顺利的进行测试,这时我们就可以使用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方法什么也不做

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

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和非空BooleananyByte()匹配任何byte和非空ByteanyCollection()匹配任何非空CollectionanyDouble()匹配任何double和非空DoubleanyFloat()匹配任何float和非空FloatanyInt()匹配任何int和非空IntegeranyList()匹配任何非空ListanyLong()匹配任何long和非空LonganyMap()匹配任何非空MapanyString()匹配任何非空Stringcontains(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)重置Mockspy(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.参考

Mockito 中文文档

JUnit + Mockito 单元测试

转载请注明原文地址: https://www.6miu.com/read-1150139.html

最新回复(0)