什么是单元测试
单元测试是由一组独立的测试构成,每个测试针对软件中的一个单独的程序单元。单元测试并非检查程序单元直接是否能够合作良好,而是检查单个程序单元的行为是否正确。
事实上,单元测试是一种验证行为,测试和验证程序中的每一项的正确性。
为什么要进行单元测试
对于单元测试,人们往往存在很多误解:
- 浪费时间太多,本身项目的时间就很紧张,没有时间去写单元测试。
- 过度的依赖测试人员,认为软件开发人员不应该参与单元测试。
- 认为单元测试不必要,代码写得很好了,no bug,no warning。
- 老代码结构混乱,耦合度高,为了写单元测试修改代码结构,意义不大,投入跟产出不成比例。
单元测试真的这么鸡肋么?No,No,No!!!
试想
- 测试人员给你报了一个bug,但是由于之后的merge失误导致代码丢失,或者别人修改代码导致这个bug再次复现。
- 重构代码的时候,被bug淹没。造成你持续不断的改bug,持续不断的加班。
明明很正常的功能,怎么现在突然不能用了?是接口的问题,还是有人修改了这个功能的逻辑?
。。。
如果你也经常遇到这些困惑,那么你就需要对项目进行单元测试了。
因为单元测试具有以下优势:
帮助理解需求
单元测试应该反映Use Case,把被测单元当成黑盒测试其外部行为。
提高实现质量
单元测试不保证程序做正确的事,但能帮助保证程序正确地做事,从而提高实现质量。
测试成本低
相比集成测试、验收测试,单元测试所依赖的外部环境少,自动化程度高,时间短,节约了测试成本。
反馈速度快
单元测试提供快速反馈,把bug消灭在开发阶段,减少问题流到集成测试、验收测试和用户,降低了软件质量控制的成本。
利于重构
由于有单元测试作为回归测试用例,有助于预防在重构过程中引入bug。
文档作用
单元测试提供了被测单元的使用场景,起到了使用文档的作用。
对设计的反馈
一个模块很难进行单元测试通常是不良设计的信号,单元测试可以反过来指导设计出高内聚、低耦合的模块。
怎么进行单元测试
Android 单元测试分类
Android 单元测试分为两大类:
1 | app/src |
Local test:
运行在本地的JVM虚拟机上,不依赖Android框架。
Instrumented tests:
通过Android系统的Instrumented测试框架,运行测试代码在真实手机上。
Android Junit + Mockito + Powermock单元测试方案
Junit + Mockito + Powermock简介
Junit 是一个Java语言的单元测试框架
Mockito 是一个Mock框架,我们可以通过Mockito框架创建配置mock对象。
Powermock 可以针对static,final,private方法进行mock
Junit + Mockito + Powermock使用
强烈建议你熟读以下内容,来熟悉Junit + Mockito + Powermock的使用。
- Mockito 中文文档 ( 2.0.26 beta )
- Mockito reference documentation
- powermock wiki
- Unit tests with Mockito - Tutorial
比如说我们要对Calculate类进行单元测试
1 | public class Calculate { |
测试Public 变量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public void testPublicField() {
assertEquals(mCalculate.mPublic, 0);
assertEquals(mCalculate.mPublicFinal, 0);
assertEquals(Calculate.mPublicStatic, 0);
assertEquals(Calculate.mPublicStaticFinal, 0);
mCalculate.mPublic = 1;
Calculate.mPublicStatic = 2;
assertEquals(mCalculate.mPublic, 1);
assertEquals(mCalculate.mPublicFinal, 0);
assertEquals(Calculate.mPublicStatic, 2);
}测试Public 方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20@Test
public void testAddPublicMethod() {
//when
when(mCalculate.addPublicMethod(anyInt(), anyInt()))
.thenReturn(0)
.thenReturn(1);
//call method
for (int i = 0; i < 2; i++) {
//verify
assertEquals(mCalculate.addPublicMethod(i, i), i);
}
//verify
verify(mCalculate, times(2)).addPublicMethod(anyInt(), anyInt());
verify(mCalculate, atLeast(1)).addPublicMethod(anyInt(), anyInt());
verify(mCalculate, atLeastOnce()).addPublicMethod(anyInt(), anyInt());
verify(mCalculate, atMost(2)).addPublicMethod(anyInt(), anyInt());
}
测试Public 返回Void 方法
1
2
3
4
5
6@Test
public void testAddPublicVoidMethod() {
//when
doNothing().when(mCalculate).voidPublicMethod(anyInt(), anyInt());
}
测试Public Static 方法
1
2
3
4
5
6
7
8
public void testAddPublicStaicMethod() throws Exception {
PowerMockito.mockStatic(Calculate.class);
PowerMockito.when(Calculate.class, "addPublicStaticMethod", anyInt(), anyInt())
.thenReturn(0)
.thenReturn(1);
}
测试Private Static 变量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public void testPrivate() throws IllegalAccessException {
PowerMockito.mockStatic(Calculate.class);
assertEquals(Whitebox.getField(Calculate.class, "mPrivate").getInt(mCalculate), 0);
assertEquals(Whitebox.getField(Calculate.class, "mPrivateFinal").getInt(mCalculate), 0);
assertEquals(Whitebox.getField(Calculate.class, "mPrivateStatic").getInt(null), 0);
assertEquals(Whitebox.getField(Calculate.class, "mPrivateStaticFinal").getInt(null), 0);
Whitebox.setInternalState(mCalculate, "mPrivate", 1);
Whitebox.setInternalState(Calculate.class, "mPrivateStatic", 1, Calculate.class);
assertEquals(Whitebox.getField(Calculate.class, "mPrivate").getInt(mCalculate), 1);
assertEquals(Whitebox.getField(Calculate.class, "mPrivateFinal").getInt(mCalculate), 0);
assertEquals(Whitebox.getField(Calculate.class, "mPrivateStatic").getInt(null), 1);
assertEquals(Whitebox.getField(Calculate.class, "mPrivateStaticFinal").getInt(null), 0);
}
测试Private 方法
1
2
3
4
5
6
7
8
9
10
public void testAddPrivateMethod() throws Exception {
PowerMockito.mockStatic(Calc.class);
//when
PowerMockito.when(mCalculate,"addPrivateMethod",anyInt(),anyInt())
.thenReturn(0)
.thenReturn(1);
}
测试Private static 方法
1
2
3
4
5
6
7
8
public void testAddPrivateStaicMethod() throws Exception {
PowerMockito.mockStatic(Calculate.class);
PowerMockito.when(Calculate.class, "addPrivateStaticMethod", anyInt(), anyInt())
.thenReturn(0)
.thenReturn(1);
}
对单例进行mock
比如对以下代码进行mock
1 | public class Singleton { |
1 | public class SingletonUse { |
测试类
1 | (PowerMockRunner.class) |