原文地址:
http://alexzh.com/tutorials/android-testing-mockito-robolectric/
译文
这是Android测试项目系列文章的第二篇。你可以在这里(http://alexzh.com/tutorials/android-testing-unit-testing/)读到第一篇关于“单元测试”的文章。所有的源代码都托管到Github。Github上面的项目有不同的分支:
app的基本版本:
https://github.com/AlexZhukovich/SimpleCoffeeOrderTestProject/tree/1_simple_coffee_order
有单元测试的基本版本app:
https://github.com/AlexZhukovich/SimpleCoffeeOrderTestProject/tree/2_unit_tests
基本版本 + payment activity:
https://github.com/AlexZhukovich/SimpleCoffeeOrderTestProject/tree/3_simple_coffee_order_paymentActivity
mockito and robolectric tests:
https://github.com/AlexZhukovich/SimpleCoffeeOrderTestProject/tree/4_mockito_and_robolectric_tests
unit testing的局限性
Unit test测试的类必须是独立性的。虽然我们常常可以达到测试的目的,但是Java类往往需要依赖其他很多类。为解决这个问题,我们需要多次测试。
不同的对象(test中的各种对象)
dummy object(虚拟对象)被传来传去但是从未被使用过,意思是他的方法从来没有被使用过,对象就像参数一样被传递。
fake object具有工作实现,但是通常被简化。
stub class是类或接口的部分实现,目的是用于测试。
mock object是为方法调用定义的类或接口的虚拟实现。
生成Mock Object对象
你可以手动创建一个mock对象或使用Mock框架来模拟这个类,其实一个mock对象的就是一个数据的提供者。应用在生产中使用真实的数据库,但是在测试中使用mock对象来模拟数据库以确保测试条件总是相同的。
Mocking框架
Mock框架要求创建mock对象尽可能的简单。比较常用的框架有:
EasyMock:http://easymock.org/,
Mockito:http://mockito.org/.
Mockito的简单介绍
Mockito(http://mockito.org/)是目前比较常用mock框架,它可以与JUnit一起使用。Mockito(http://mockito.org/)允许你自己创建并配置mock对象。
项目中添加Mockito依赖
如果你使用的是gradle系统构建的,在你的“build.gradle”文件中添加下面的依赖:
1. 使用Mockit API
(1)创建并配置mock对象
Mockito允许使用static mock()方法来创建mock对象;
or
执行when(...).thenReturn(...)方法需要建立明确的传入特定条件并返回本次测试的值。你也可以使用像anyInt(),anyFloat(),anyList()等这样的方法明确输入值的类型并返回却确定的值;
doReturn(…).when(…).methodCall方法工作原理类似,但在void方法(无参数返回的方法)中比较常用。
doThrow主体也可用于无参返回的方法void,可以捕获方法中的异常。
(2)确保调用mock对象
你也可以使用verify()方法去保证该方法一定被调用
(3)限制
Final classes(final 类)
Anonymous classes (匿名类)
Primitive types (基本类型)
下面是Android Test的一些限制
如果我们运行这个测试,你会发现如下的错误提示信息。
仍然有很多基本的类的方法不能被模拟,但是可以使用Robolectric来代替。
2. Robolectric
在Android中进行单元测试一直比较困难,但是使用Rebolectric相对来说比较容易。
这个库还允许你通过删除抛出RuntimeExceptions的 stubs 来模拟 Android SDK的API。
Robolectric是一个运行在JVM上面的单元测试的三方库。同时该库也允许进行测试驱动开发(TDD)。
Robolectric通过所谓的shadow对象替换了所有的Android类。
如果一个方法是由Robolectric实现的,它会将这些方法调用转发给shadow对象,该对象的行为类似于Android SDK的对象。如果shadow对象未实现某个方法,则只返回默认值,如 null 或 0;
接下来看看一下如果在Android项目中如何配置Robolectric,以下是最简单的在build.gradle配置方法。
如果你想使用 Shadow对象,需要在项目中添加Maven依赖
接着,就可以使用Robolectric来创建 Android test。在同名tests包下,你可以创建一个新类(android test包是用来放instrumental test代码,而test包是用来放local JVM 测试代码)。接着也通过注解的方式为该类创建一个Runner和config,来使test可执行。
我们同时也需要为应用添加一些改动。首先,我想要增加一个付款页面,为此需要增加布局文件、activity文件、修改AndroidManifest文件配置:
activity_payment.xml布局:
PaymentActivity.java
最后在AndroidManifest.xml文件注册activity.
如果我们要在MainActivity中的 “Pay“ 按钮添加并绑定点击事件来执行下一步的操作。我使用下面这行代码来打开一个新的activity。
第一个Robolectric测试:我们可以检测MainActivity是否存在。在这个例子中,我们需要创建一个MainActivity类来检测。接下来就需要对比这个对象是否为null。
接下来需要测试验证的就是价格标签,这个应用中有2个标签用来显示价格。
下一个例子验证咖啡数量选择器(加号按钮,数量标签,减号按钮 三个功能)。
下一个测试例子验证 增加按钮和减少按钮如何工作。
下一个测试例子验证支付按钮是否存在。
下一个测试例子验证Intent启动PaymentActivity是否正确。
最后一个例子测试 点击 “pay”按钮后 MainActivity是否能打开 PaymentActivity。
在下面的类中你可以找到全部的源码。
然而,上面这个源码写得并不是那么好,接下来我将修改一下这段代码。在之前的例子中,几乎所有的方法都有activity的创建和初始化的代码。但是一些View的初始化,如TextView和Buttons,这些部分的代码都可以放在有@Before注解的方法中。因为@Before注解的方法是在每个测试之前执行的。下面MainActivityTest代码片段做了一些修改:
在写接下来的测试之前,我们需要了解一些创建activity有很多种方法。最简单的方法你可以使用setupActivity(Class activityClass)方法床架activity,但是如果你想使用Intent传递更多的数据,这就不能满足我们的需求。但是还可以使用另一种方法,buildActivity(Class activityClass),以及一些链式操作,如:withIntent(SOME_INTENT),create(),start(),resume(),visible(), andget()。当然,你可以在这个链式操作中,任意改变或者增加一些方法。同时setupActivity方法也可以触发一系列的动作:create(),start(),postCreate(null),resume(),visible(), andget().
我们可以开始为PaymentActivity写测试。
我们已经完成了这个App的所有测试。现在我们可以调试所有的测试,此时你可以看到所有的测试是否正确运行。
源码托管在Github(https://github.com/AlexZhukovich/SimpleCoffeeOrderTestProject),可在不同分支查看。
资源:
Mockito:http://site.mockito.org/
Robolectric:http://robolectric.org/
Unit testing with Mockito:
http://www.vogella.com/tutorials/Mockito/article.html
领取专属 10元无门槛券
私享最新 技术干货