单元测试业界标准:
MVP + Junit4 + Mockito + Hamcrest + Espresso + Dragger2
一、为何要做单元测试
- App持续集成的时候需要一个集成测试保障其正确性(正确性)
- 页面较复杂的时候,我们是否可以先测试业务逻辑的正确性(边开发边测试)
- 项目较大,编译缓慢,测试业务逻辑部分(测试速度大幅提高)
- 对代码结构、代码健壮性、代码可维护度都有很大提高
不用太担心修改代码后,会导致其它bug,单元测试集成测试会告诉我们
other:蘑菇街的哥们说过:慢慢的你会发现,其实不是这样的,纯java的代码其实真不少,而且往往是核心的逻辑所在
二、测些什么
代码结构:
Android中的MVC分层不清晰,我们更倾向于MVP,这里列举一些比较好的文章。
测试框架:
Android单元测试分为:
Local Unit Tests (直接在JVM上进行测试):
1.Junit4
2.Mockito (最常用的Mock之一)
3.Hamcrest(增加JUnit的测试能力)
4.Robolectric(第三方:JVM中模拟Android)
Instrumented Tests (需要借助仪器、模拟器测试):
1.AndroidJUnitRunner (官方:InstrumentTestRunner升级版,支持Junit4,下面的UI测试框架需要与之结合使用)
2.Espresso (官方:测试单独App)
3.UI Automator (官方:跨进程测试(多App之间进行测试))
4.Robotium(第三方:基于Android 原生的Instruments)
5.Appium (第三方:支持Android IOS自动化测试)
- OS: Apple’s UIAutomation
- Android 4.2+: Google’s UiAutomator
- Android 2.3+: Google’s Instrumentation. (Instrumentation support is provided by bundling a separate project, Selendroid)
测些什么
- Local Tests 测试业务逻辑层、部分Model层
- Instrumented Tests 测试UI,测试用例
- 测试方法的返回值
- 测试操作流程中方法的调用情况(是否调用?调用几次?被调用时传入的参数值)
三、测试准备
1. Junit4
- Assert.assertXXX :断言
- 参数化测试
- @Suite.SuiteClasses
- JUnit Rules之Github
- JUnit Rules源码分析
2. Mockito
Mock的概念(Mockito.mock):所谓的mock就是创建一个类的虚假的对象,在测试环境中,用来替换掉真实的对象,以达到两大目的:
- 验证这个对象的某些方法的调用情况,调用了多少次,参数是什么等等
- 指定这个对象的某些方法的行为,返回特定的值,或者是执行特定的动作
- 注意:需要将Mock对象注入到需要测试的类中
@RunWith(MockitoJUnitRunner.class)
@Mock > (Mock出一个对象,此对象的方法调用并不会真正调用)
@Spy > (Spy出的对象,调用其方法会真正执行)
@Captor > (用于抓取被调用方法的参数,进行验证,也可以使用ArgumentCaptor new出来)
@InjectMocks > (只要在被测试类上标记@InjectMocks,Mockito就会自动将标记@Mock、@Spy等注解的属性值注入到被测试类中。)
// 抓取mock对象方法的参数进行验证:
// 验证mTasksRepository对象调用了getTask,并且抓取此方法的第二个参数
verify(mTasksRepository).getTask(eq(testTask.getId()), mGetTaskCallbackCaptor.capture());
// 调用上一步抓取到的参数的onTaskLoaded方法
mGetTaskCallbackCaptor.getValue().onTaskLoaded(testTask);
// 预设mock对象某方法被调用时返回特定值:
Mockito.when(searchSchoolFragmentView.isActive()).thenReturn(true);
// 预设mock对象某方法被调用时执行doAnswer里面自定义的逻辑:
// 也可以先
Mockito.doAnswer(new Answer() {
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
//这里可以获得传给performLogin的参数
Object[] arguments = invocation.getArguments();
//callback是第三个参数
NetworkCallback callback = (NetworkCallback) arguments[2];
callback.onFailure(500, "Server error");
return 500;
}
}).when(mockUserManager).performLogin(anyString(), anyString(), any(NetworkCallback.class));
3. Hamcrest
Assert.assertThat(object,Matcher.xxxmethod(yy));
Matchers:匹配器 > Espresso也基于此
4. Espresso
组成部分:
- ViewMachers:寻找用来测试的View
- ViewAction:发送交互事件
- ViewAssertions:检测测试结果
View Machers: withId、withHint、allof(withId(),withHint())…
View Actions: click()、doubleClick()、pressBack()、openLick()…
View Assertions: matches、not、isDisplayed…
线程安全保障:
Espresso测试有个很强大的地方是它在多个测试操作中是线程安全的。
Espresso会等待当前进程的消息队列中的UI事件,并且在任何一个测试操作中会等待其中的AsyncTask结束才会执行下一个测试。这能够解决程序中大部分的线程同步问题。
Espresso中有个API叫做registerIdlingResource,它可以让你使用自定义的线程安全逻辑。(上面链接中有对registerIdlingResource详细讲解)
四、参考资料
蘑菇街小创
http://chriszou.com/
Android单元测试
http://www.chriszou.com/2016/04/13/android-unit-testing-start-from-what.html
解读Android官方MVP项目单元测试
http://www.jianshu.com/p/cf446be43ae8
Android官网测试文档
http://developer.android.com/intl/zh-tw/training/testing/start/index.html
dragger2:
(一)http://www.jianshu.com/p/cd2c1c9f68d4
(二)http://www.jianshu.com/p/1d42d2e6f4a5
(三)http://www.jianshu.com/p/65737ac39c44
dragger2:
http://www.cnblogs.com/tiantianbyconan/p/5092525.html
Android谷歌官方测试demo:
https://github.com/googlesamples/android-testing
Mockito讲解
http://www.vogella.com/tutorials/Mockito/article.html
Mockito更详细讲解
http://hotdog.iteye.com/blog/search?query=Mockito
Espresso
http://blog.csdn.net/shandong_chu/article/details/47083753
Espresso IdlingResource
http://www.jianshu.com/p/95d075b90a2f
MVP快速开发框架Beam
https://github.com/Jude95/Beam
encleus简化MVP
https://github.com/konmik/nucleus