AndroidTest
Android 中单元测试并不常见, 这篇文章就我自己的知识范围来介绍:
JUnit
在学习 Java 时就知道这是一个用来给纯 Java 测试的工具, 在 Android 中一样使用, Android Studio 可以用快捷键 cmd + enter
为每个类自动创建测试类. 在测试类中, 一般使用 Assert
来断言每个条件的对错, 而 JUnit 的注解则为单元测试提供框架.
@Test
定义测试单元(每个方法为一个单元用例), 接受参数有
expected 预期会抛出某个异常, 不抛出则报错
1
Class<? extends Throwable> expected() default None.class;
timeout 超时
1
long timeout() default 0L;
@Before
每个 Test 方法执行之前都会调用, 可以做预处理操作, 必须修饰 public 方法
@After
每个 Test 方法执行之后都会调用, 可以做清理操作
@BeforeClass
由于连续测试可能需要共享一个变量, 或者每个测试单元执行前都需要很长时间的准备工作, 可以把这些准备工作移到 BeforeClass 中, 该注解必须注解于一个 public static void 且没有参数的方法.
@AfterClass
用于 @BeforeClass 的清理工作
@Ignore
用于执行测试的时候忽略某个 @Test 单元
@Rule
定义
@Test
单元执行时的逻辑,org.junit.rules
包定义了一些常用的 Rule 规则, 但我们也可以自定义 Rule.1
2
3
4
5
6
7
8public class RuleSample implements TestRule {
public Statement apply(Statement base, Description description) {
base.evaluate();
return null;
}
}
Mockito
Mock 即[模拟]的意思, 当某些类因为依赖太多等关系难以创造, 或者我们只需要一个类的对象时, Mock 便是一个很好的工具, 可以帮助我们隔离代码进行测试. 而 Mockito 便是一个 Android 常用的 Mock 工具类.
模拟对象
Mockito 支持多种模拟对象方法.
@Mock
直接注解对象. 注解对象需要初始化, 可以有四种初始化方法.在
@Before
注解的初始化方法中使用MockitoAnnotations.initMocks(this);
使用自带的
@Rule
初始化1
2
public MockitoRule mockitoRule = MockitoJUnit.rule();使用
@RunWith
注解测试类.1
2
3
4
public class Test {
}使用
Mockito.mock()
初始化对象.
@InjectMock
创建一个实例, 其余用@Mock
或@Spy
注解创建的 mock 对象将被注入到用该实例中.
设置桩
Mockito 支持以下方法设置桩
1 | Mockito.doXXX().when(XXX) |
举个例子, 如果希望 TextView
在调用 getText()
时返回特定的内容, 可以使用
1 | Mockito.when(mTextView.getText()).thenReturn("123); |
桩可以设置多次, 最终只会返回最后一次设置的值.
验证
Mockito 支持以下方法来验证函数执行
1 | public static <T> T verify(T mock) |
默认的 VerificationMode 即 times(1)
, 即方法执行了一次, sdk 也提供了几种默认的验证模式实现, 如 never()
, atLeastOnce()
等.
参数匹配
为了模拟某些参数的输入, 可以匹配这些参数的输入, 并返回所需的值. ArgumentMatchers.any()
是一个比较常用的方法.
比如模拟 TextView 的 OnClickListener().
1 | Mockito.doAnswer(new Answer() { |
@Spy
Mockito.spy() 返回的对象, 除非该方法已经有设置桩, 否则会调用该对象的真实方法. 可以用来改变对象特定方法的返回值, 而不改变对象本身. 注意 spy 不能 mock final 方法.
1 | List list = new LinkedList(); |
JMockit
Mockito 的语法虽然比较简单易懂, 但它支持的功能还是不够多, 有一部分人是配合 PowerMock 一起使用, 但还有一个更强大的 Mock 工具值得使用.
模拟对象
注意 @RunWith
注解初始化测试类.
@Mocked 注解对象, 除了基本类型和数组对象, 其余所有都可以 mock, 会 mock 类中全部方法及其父类. 可以指定
stubOutClassInitialization
来决定 mock 对象时是否需要初始化静态变量, 某些 JNI 调用的静态变量在 mock 时初始化可能为 false, 这时候可以指定为 true 来跳过初始化.@Injectable 仅 mock 指定的对象, 对于可以传入的对象, 使用 @Injectable 比 @Mock 更好
@Capturing mock 类及其子类, 也可以 mock 接口, 可以指定 maxInstances 来决定需要 mock 多少个对象.
常规使用
JMockit 用法是 录制 - 执行 - 验证 (record - replay - verify).
1 |
|
参数匹配
参数匹配有很多, 最宽松的是 anyXXX , 其次是 withXXX.
使用 null
时, 必须有一个 anyXXX 或者 withXXX .
MockUp API
JMockit 我觉得 MockUp Api 特别强大, 几乎可以 Mock 所有的方法, 静态方法, 构造函数, 私有函数都可以, 这也是这个工具强大的地方.
比如有如下测试类:
1 | class Test { |
那么可以通过这样去 mock 对应的方法:
1 | new MockUp<Test>() { |
一些 Tip
如果只是某个方法需要某个 mock 过的变量, 可以在测试方法的入参中传入, 不需要写成全局变量.
1
2
3
4
public void test( TextView textView){
}