去年的一个电商的项目做完了,已经上线了,客户回来找我们做单元测试,性能测试,安全性测试,一堆测试项。我的内心是崩溃的,项目都上线了,隔了几个月了,项目组都换了两批了。没办法,客户是上帝,于是研究了一下Android目前的一些测试规范,记录一下吧。
理清代码逻辑,让程序更加健壮,方法变更后跑一遍测试,若结果正确则证明没有影响到别的方法,更快的排查错误。
测试分类:
目标组件生命周期,异步任务,消息传递,功能执行,界面跳转,计算结果
单元测试的目标函数分类:
测试框架:
Robolectric是一个Android单元测试框架。
可以测试Activity的跳转、Activity展示View(包括菜单)和Fragment到View的点击触摸以及事件响应,同时Robolectric也能测试Toast和Dialog。对于需要网络请求数据的测试,Robolectric可以模拟网络请求的response。对于一些Robolectric不能测试的对象,比如ConcurrentTask,可以通过自定义Shadow的方式现实测试。
首先添加依赖
testCompile 'junit:junit:4.10'
testCompile 'org.robolectric:robolectric:3.0'
Mock配置如果要测试的目标对象依赖关系较多,需要解除依赖关系,以免测试用例过于复杂,用Robolectric的Shadow是个办法,但是推荐更加简单的Mock框架,比如Mockito,该框架可以模拟出对象来,而且本身提供了一些验证函数执行的功能。Mockito配置如下:
repositories {
jcenter()
}
dependencies {
//don't use androidTestCompile here!!!
testCompile "org.mockito:mockito-core:1.+"
}
网络连接测试
计算结果测试
响应动作测试
点击跳转,点击弹窗
问:robolectric与Robotium区别?
答:这是两种测试框架,Robotium依赖Android平台,robolectric不依赖
问:testCompile 与 AndroidTestCompile 区别?
答:testCompile作用在 unit tests (src/test)
androidTestCompile作用在 test api (src/androidTest)
问题:java.lang.NullPointerException: parentLoader == null && !nullAllowed
解决方案,不要使用androidTestCompile,意味着你创建的Test类不要放在AndrodTestCase下面
testCompile ‘org.robolectric:robolectric:3.3.2’
问题:首次执行 Robolectric回加载很长时间
因为 Robolectric又去maven仓库下了一些依赖包,那个仓库地址是国外的所以很慢
解决方案,把下载地址改为国内的加速Robolectric下载依赖库及原理剖析
问题:No such manifest file: build\intermediates\bundles\debug\src\main\AndroidManifest.xml
解决:配置一下sdk,目前只支持到21 @Config(constants = BuildConfig.class, sdk = 21)
问题:java.lang.NoClassDefFoundError: com/amazon/device/messaging/ADMMessageReceiver
三方库找不到问题,项目大了用的库太多,还有maven进来的,解决方法是配置@Config{Libraries} 但是我没有试成功,应该是路径问题,没找到解决办法
MD,Robolectric配了半天没配好,各种问题,最后一个三方库找不到的问题实在懒得搞了,以后再看。用robotium也符合要求。
很简单,依赖这个库
androidTestCompile 'com.jayway.android.robotium:robotium-solo:5.6.2'
在需要测试的类上右键->goto->Create test
或者直接在androidTest下面创建测试类
package com.xxx.xxx;
import android.content.Intent;
import android.support.test.InstrumentationRegistry;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
import android.test.ActivityInstrumentationTestCase2;
import com.robotium.solo.Solo;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
public class LoadingActivityTest extends ActivityInstrumentationTestCase2 {
private Solo solo;
@Rule
public ActivityTestRule mActivityRule = new ActivityTestRule<>(
LoadingActivity.class,
false,
false);
@SuppressWarnings("unchecked")
public LoadingActivityTest() throws ClassNotFoundException {
super(LoadingActivity.class);
}
@Before
public void setUp() throws Exception {
solo = new Solo(InstrumentationRegistry.getInstrumentation());
}
@After
public void tearDown() throws Exception {
if (mActivityRule.getActivity() != null) {
mActivityRule.getActivity().finish();
}
}
@Test
public void testSplash() {
Intent intent = new Intent("com.xx.xxx.LoadingActivity");
mActivityRule.launchActivity(intent);
solo.assertCurrentActivity("is launch loadActivity", LoadingActivity.class);
Assert.assertTrue("LoadingActivity launch fail", solo.waitForActivity(LoadingActivity.class, 1500));
}
}
直接右键run就可以了,绿条通过红条不通过。主要就是Solo这个类,它包含了很多的测试方法
启动界面
@Rule
public ActivityTestRule mActivityRule = new ActivityTestRule<>(
StoreActivity.class,
false,
false);
@Test
public void launcherActivity() {
Intent intent = new Intent("com.xxx.xxx.TestActivity");
mActivityRule.launchActivity(intent);
}
点击
首先在setUp中申明一个Solo对象。
@Before
public void setUp() throws Exception {
solo = new Solo(InstrumentationRegistry.getInstrumentation());
}
solo.clickOnText(String str);
在当前可见页面上搜素字符传,并点击这块内容
solo.clickOnView(View v);
点击这个View,View必须在当前页面可见
solo.clickInList(int line,int listIndex,int id);
点击可见页面上第listIndex个listView的第line行,布局是id的控件
solo.clickInRecycle(int index);
与上面的方法类似,只是这个是适用于界面上是RecycleView的
断言
solo.assertCurrentActivity(String str,Class activityClass);
断言当前的Activity是activityClass这个,不是则报错
Assert.assertTrue(boolean condition);
断言当前这个是正确的
solo.assertMemoryNotLow();
断言目前系统可用内存是否过低,内存空间足够则通过
solo.isCheckBoxChecked(int index);
判断checkBox是否处于被选中的状态,可以通过index和text两种方法定位
其余方法
solo.sleep(int time);
当前操作睡time的时间,单位是毫秒,和thread一样,一般用于在点击之后等待数据响应或界面跳转需要的时间
solo.waitForView(View v);
在这一步等待界面上的v被加载出来,默认超时时间是20s
solo.waitForLogMessage(String str);
等待控制台打印出str的log信息,默认超时20s,一般用于网络访问时间或一些耗时计算的等待
solo.goBack();
按下返回键
solo.searchText(String str);
判断当前的屏幕中是否能找到指定的text
项目根目录执行gradlew clean createDebugCoverageReport
然后就是漫长的等待,项目开始清理,并运行所有的测试项。
因为是用的robotium,所以必须有一台Android设备是链接的,不然运行半天最后test case跑不起来。
然后你就看到界面在那自己各种跳各种点各种测,很爽啊。