当前位置: 代码迷 >> 综合 >> Android单元测试——Junit+Mock
  详细解决方案

Android单元测试——Junit+Mock

热度:47   发布时间:2023-11-23 20:14:41.0

首先是单元测试很重要!很重要!很重要!

目前主流的是Junit4 来进行Java的单元测试

首先需要导入的包有

import org.junit.Test;     import static org.junit.Assert.*;//引入断言

不同于Junit3,测试类不需要再继承TestCase类,可以直接声明,此外,测试方法也不需要再以test开头,但是为了方便使用,最好以test<TestMethod>的形式来命名,例如,要测试add方法是否正确,测试方法名最好为?testAdd?这种(如果使用Android Studio的话可以自动生成)。

  • 关于Junit4的一些重要的注解
@Before
在任何一个单元测试方法执行之前都要执行的代码,主要做一些初始化的工作@After
在任何一个单元测试方法执行之后都要执行的代码,主要做一些收尾工作@BeforeClass
public static void xxxx()//必须声明为该形式
在所有的单元测试执行之前执行的代码,仅执行一次@AfterClass
public static void xxxx() //必须声明为该形式
在所有的单元测试都执行完后执行的代码,仅执行一次@Ignore
用来标记某测试方法不参与此次的测试,在运行结束后会通知有几个测试被忽略整个测试的执行顺序为@BeforeClass->(@Before->@Test->@After)->@AfterClass|_______________________| 这里对于每个测试方法都会执行一次
  • Junit4中的一些断言 
检查对象是否为空
assert(Not)Null(java.lang.Object object)检查值是否相等
assertEquals()检查对象是否相等
assert(Not)Same(java.lang.Object expected, java.lang.Object actual)检查条件是否为真
assertTrue(False)(boolean condition)对于@Test有一些额外的参数可以使用@Test(expected = Exception.class)//测试方法若没有抛出Annotation中的Exception类型(子类也可以)->失败@Test(timeout=100)//一般用来性能测试,如果方法耗时超过了timeout会导致失败关于仪器化测试以及本地测试在Android Studio中创建工程时已经提供了本地测试和仪器化测试两种测试,分别在src/androidTest     //仪器化测试src/test            //本地测试
  • 关于二者的区别:

如果测试用例需要访问仪器(instrumentation)信息(如应用程序的 Context ),或者需要 Android 框架组件的真正实现(如 Parcelable 或 SharedPreferences 对象),那么应该创建仪器化单元测试,由于要跑到真机或模拟器上,所以会慢一些。

在AS中运行时只需要到相关的测试方法上点击右键即可,如果是本地化测试的话一般可以直接在Run窗口看见结果,如果是仪器化测试的话需要选择运行的设备,然后执行以下操作

  1. 向模拟器(真机)安装两个apk,分别是app-debug.apk和app-debug-androidTest.apk,instrumented测试相关的逻辑在app-debug-androidTest.apk中。
  2. 安装完这两个apk后,通过?am instrument?命令运行instrumented测试用例
  3. 运行之后就可以在Run窗口看见结果

But But 一般来说仪器化测试会很慢(目前还没有感受到,可能之后会体会到),因此嘞,如何既能够拥有本地化测试的速度,又可以拥有仪器化测试的效果嘞?这就需要用到模拟技术,下面介绍几种比较流行的模拟技术

Mock技术

我们知道在调用某个单元测试方法时,这个方法可能会对其他的对象产生了许多依赖,参考如下的例子

我们在对BankService的某个方法进行测试时,可能会用到BankDao,AccountService,AuthService,

一般情况下,我们可以采用的方法是把这几个类都创建出来,然后就和实际运行情况一样了,但是可以想象如果在依赖很复杂的情况下,会非常的麻烦,与单元测试的原本意图不符,这个时候,就可以使用Mock来创建MockObject。

 

如上的例子在使用mock之后就变为

我们不需要真正的创建实例,只需要模拟对应的方法即可。

Mocktio

在使用之前需要自build.gradel加入以下依赖

testImplementation 'org.mockito:mockito-core:2.19.0'

同时需要在文件中添加如下包

import static org.mockito.Mockito.*;

为测试类加上以下注解

@RunWith(MockitoJUnitRunner.class)

对于Mockito主要有如下的几种语法

  • 对象声明

mockito中存在着两种类型,一种是mock,一种是spy

在使用上对于mock一般是,加入要mock类A,使用方法是

A mockA = mock(List.class)//此时只是模拟了类A,并没有真正的创建A

对于spy使用方法是

A a = new A();A spyA = spy(a);

关于二者的区别https://codeday.me/bug/20180907/245994.html

  • 函数调用
when(x).thenReturn(a).thenReturn(b).......//表示当x第一次发生时返回a,第二次发生时返回b,以此类推,//这里的x表示调用的函数(函数+参数唯一标志), a表示返回值,从这里也可以发现,使用mock并没有真正的调用函数,只是模拟了返回值doReturn().when();     //与上述类似,区别在于上述是mock对象,这个是spy对象,同理,也是模拟了返回值doCallRealMethod().when();//针对mock对象,这里是真正调用了该方法
  • 抛出异常
doThrow(new Exception).when(x); //表示当x发生的时候抛出异常
  • 返回值为void
doNothing().when()
  • 校验mock方法的调用
verigy(《Object》, 《time》).《function》(《param》);//《Object》表示校验的对象//《function》(《param》)表示校验的行为//《time》表示校验的条件,例如//-atLeastOnce():表示function至少被调用了一次//-times(x):表示function被调用了x次//-never():表示function从未被调用

Talk is cheap, show me your code!

mock使用实例

@Testpublic void configMockObject() {List mockedList = mock(List.class);// 我们定制了当调用 mockedList.add("one") 时, 返回 truewhen(mockedList.add("one")).thenReturn(true);// 当调用 mockedList.size() 时, 返回 1when(mockedList.size()).thenReturn(1);Assert.assertTrue(mockedList.add("one"));// 因为我们没有定制 add("two"), 因此返回默认值, 即 false.Assert.assertFalse(mockedList.add("two"));Assert.assertEquals(mockedList.size(), 1);Iterator i = mock(Iterator.class);when(i.next()).thenReturn("Hello,").thenReturn("Mockito!");String result = i.next() + " " + i.next();//assertAssert.assertEquals("Hello, Mockito!", result);}

mock校验实例

@Testpublic void testVerify() {List mockedList = mock(List.class);mockedList.add("one");mockedList.add("two");mockedList.add("three times");mockedList.add("three times");mockedList.add("three times");when(mockedList.size()).thenReturn(5);Assert.assertEquals(mockedList.size(), 5);verify(mockedList, atLeastOnce()).add("one");verify(mockedList, times(1)).add("two");verify(mockedList, times(3)).add("three times");verify(mockedList, never()).isEmpty();}

spy使用实例

@Testpublic void testSpy() {List list = new LinkedList();List spy = spy(list);// 对 spy.size() 进行定制.when(spy.size()).thenReturn(100);spy.add("one");spy.add("two");// 因为我们没有对 get(0), get(1) 方法进行定制,// 因此这些调用其实是调用的真实对象的方法.Assert.assertEquals(spy.get(0), "one");Assert.assertEquals(spy.get(1), "two");Assert.assertEquals(spy.size(), 100);}

 

  相关解决方案