Android单元测试的整理
作者:kachidokima 发布时间:[ 2017/6/12 10:19:12 ] 推荐标签:软件测试 单元测试
依赖
// Android JUnit Runner
androidTestCompile 'com.android.support.test:runner:0.5'
// JUnit4 Rules
androidTestCompile 'com.android.support.test:rules:0.5'
//一些依赖关系可能出现的冲突。在这种情况下可以在 espresso-contrib 中 exclude他们
androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
}
//并且需要在 defaultConfig 节点添加
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
并且每个测试类都需要加标注
@RunWith(AndroidJUnit4.class)
public class Test {...}
ActivityTestRule
在开始自动化测试之前都会定义一个ActivityTestRule 它用于在测试的时候launch待测试的activity
获得View
使用ViewMatcher.class里面的方法可以找到你想要的View,如你想找有Hello文字的View,你可以这样使用
onView(withText("Hello"));
相似的你也可以使用View的资源Id来找到该view
onView(withId(R.id.hello));
当有多个约束条件时,可以使用Matchers.class的allof()方法来组合,例子如下:
onView(allOf(withText("Hello") ,withId(R.id.hello)));
对View执行一些操作
对View操作的代码大概是这样: onView(...).perform();
在onView中找到这个View后,调用perform()方法进行操作,如点击该View:
onView(withId(R.id.hello)).perform(click());
也可以执行多个操作在一个perform中如
onView(withId(R.id.hello)).perform(click(),clearText());
检查View(测试与验证)
使用check()方法来检查View是否符合我们的期望 onView(...).check();
如检查一个View里面是否有文字Hello:
onView(withId(R.id.hello)).check(matches(withText("Hello")));
总之全部操作都在这个图里了
其他
1、判断这个View存不存在,返回一个boolen
//Espresso不推荐在测试使用条件逻辑,找不到而不想直接报错只能try catch
try {
onView(withText("my button")).check(matches(isDisplayed()));
//view is displayed logic
} catch (NoMatchingViewException e) {
//view not displayed logic
}
2、模拟退出Activity的返回操作
Espresso.pressBack();
3、有2个一样文字View,怎么只使用第一次找到的这个View
public static <T> Matcher<T> firstFindView(final Matcher<T> matcher) {
return new BaseMatcher<T>() {
boolean isFirst = true;
@Override
public boolean matches(final Object item) {
if (isFirst && matcher.matches(item)) {
isFirst = false;
return true;
}
return false;
}
@Override
public void describeTo(final Description description) {
description.appendText("should return first matching item");
}
};
}
//使用
onView(allOf(isDisplayed(),firstFindView(withText("Hello"))));
高阶
Intented与Intending
导包
androidTestCompile 'com.android.support.test.espresso:espresso-intents:2.2'
每次使用必须先Intents.init(),用完后必须调用Intents.release释放,或者使用的RuleActivity是IntentsTestRule,比如:
//继承自ActivityTestRule,会在每个测试前和结束自动初始化和释放
@Rule
public IntentsTestRule<ImageViewerActivity> mIntentsRule = new IntentsTestRule<>(ImageViewerActivity.class);
使用
Intending与Mockito.when相似,respondWith 相当于 thenReturn
ActivityResult result = new ActivityResult(Activity.RESULT_OK, resultData);
intending(hasComponent(hasShortClassName(".ContactsActivity"))).respondWith(result);
Intenteded与Mockito.verify相似,验证某个Intent是否发出
intended(allOf(
hasAction(Intent.ACTION_CALL),
hasData(INTENT_DATA_PHONE_NUMBER),
toPackage(PACKAGE_ANDROID_DIALER)));
Espresso中提供了许多方法用于检测Intent的各个部分,下面是每个字段的对应关系
Intent.setData <–> hasData
Intent.setAction <–> hasAction
Intent.setFlag <–> hasFlag
Intent.setComponent <–> hasComponent
一个标准的使用可以是这样
public void testLoginPass() {
ActivityResult activityResult = new ActivityResult(Activity.RESULT_OK, new Intent());
Intents.init();
intending(expectedIntent).respondWith(activityResult);
onView(withId(R.id.button_login)).perform(click());
intended(expectedIntent);
Intents.release();
onView(withId(R.id.button_login)).check(matches(withText(R.string.pass_login)));
}
其他使用
想要每个Activity启动的时候都收到某个Intent
@RunWith(AndroidJUnit4.class)
public class MainActivityTest {
@Rule
public ActivityTestRule<MainActivity> mActivityRule =
new ActivityTestRule<MainActivity>(MainActivity.class) {
@Override
protected Intent getActivityIntent() {
Context targetContext = InstrumentationRegistry.getInstrumentation()
.getTargetContext();
Intent result = new Intent(targetContext, MainActivity.class);
result.putExtra("Name", "Value");
return result;
}
};
}
可以去屏蔽掉其他包发的Intent的影响
@Before
public void stubAllExternalIntents() {
intending(not(isInternal())).respondWith(new ActivityResult(Activity.RESULT_OK, null));
}
Idling Resource
一般用在异步里面,可以再测试的时候让其不会因为延迟而导致测试失败
导包
compile 'com.android.support.test.espresso:espresso-idling-resource:2.2.2'
为了便于测试,一般都会融合在实际回调中来控制当前是否处于空闲IDLE状态,可以在Activity中加入以下方法,然后再测试中获取
//要想在测试用例中使用源码中的数据可以使用VisibleForTesting这个注释符
@VisibleForTesting
public IdlingResource getIdlingResource() {
return mIdlingResource;
}
使用
首先要实现一个IdlingResource一般app都用一个可以了,且重写三个函数:
getName():必须返回代表idling resource的非空字符串,一般直接通过class.getName()
isIdleNow():表示当前是否idle状态
registerIdleTransitionCallback(..): 用于注入回调
然后再有异步可能有延迟的地方使用IdlingResource,一般实现的时候使用Atom类来做并发的处理
后在每次测试的时候都需要在Espresso注册这个IdlingResource
@Before
public void registerIdlingResource() {
mIdlingResource = mActivityRule.getActivity().getIdlingResource();
Espresso.registerIdlingResources(mIdlingResource);
}
@After
public void unregisterIdlingResource() {
if (mIdlingResource != null) {
Espresso.unregisterIdlingResources(mIdlingResource);
}
}
RecyclerView
当要测试RecyclerView的时候需要添加如下依赖:
// Espresso-contrib for DatePicker, RecyclerView, Drawer actions, Accessibility checks, CountingIdlingResource
androidTestCompile 'com.android.support.test.espresso:espresso-contrib:2.2.2'
使用也比较简单,基本和一般view一样只是多了些方法
onView(ViewMatchers.withId(R.id.recyclerView))
.perform(RecyclerViewActions.actionOnItemAtPosition(ITEM_BELOW_THE_FOLD, click()));
onView(ViewMatchers.withId(R.id.recyclerView))
.perform(RecyclerViewActions.scrollToHolder(isInTheMiddle()));
onView(withId(R.id.recycleviews)).perform(RecyclerViewActions.actionOnHolderItem(new
CustomViewHolderMatcher(hasDescendant(withText("Name"))), click()));
后说一下项目哪些需要测
一般的逻辑与功能性代码使用JUnit+Mock
所有的Model、Presenter/ViewModel、Api、Utils等类的public方法
Data类除了getter、setter、toString、hashCode等一般自动生成的方法之外的逻辑部分
UI测试
自定义View的功能:比如set data以后,text有没有显示出来等等,简单的交互,比如click事件,负责的交互一般不测,比如touch、滑动事件等等。
Activity的主要功能:比如view是不是存在、显示数据、错误信息、简单的点击事件等,组件之间intent交互。
比较复杂的用户交互比如onTouch,以及view的样式、位置等等,一般直接人工测试。
相关推荐
更新发布
功能测试和接口测试的区别
2023/3/23 14:23:39如何写好测试用例文档
2023/3/22 16:17:39常用的选择回归测试的方式有哪些?
2022/6/14 16:14:27测试流程中需要重点把关几个过程?
2021/10/18 15:37:44性能测试的七种方法
2021/9/17 15:19:29全链路压测优化思路
2021/9/14 15:42:25性能测试流程浅谈
2021/5/28 17:25:47常见的APP性能测试指标
2021/5/8 17:01:11