android 单元测试

2018-12-19  本文已影响0人  codeflame

前言:
1.参考:
Android 单元测试实践

2.本文指androidTest,即运行在android真机/虚拟机 的测试,而非可直接运行在jvm的单纯的java test


导入依赖(包)

testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'

这几条一般自带就有。这里再增加一条:
androidTestImplementation group: 'com.android.support.test', name: 'rules', version: '1.0.2'
注意依赖方式是androidTestImplementation,具体差异右转百度/官方文档。另外版本可自行上maven中央仓库查询后更改至最新。

相关类的意义

Espresso:与视图交互的入口(通过onView和onData)。

ViewMatchers: 相当于代表着一个View(如TextView等)。

ViewActions:代表着触发的动作(如click-点击、typeText-输入文本等)。

ViewAssertions:断言(用于判断等价之类的)

ViewInteraction:视图交互对象

注意:
1.主要是使用它们的静态方法,如onView、onData、withId等,详细请查看官方文档。另外Android Studio对静态方法不太敏感,可能不会提示你导入相关类静态方法,此时请手动增加相关静态方法导入,如

import static android.support.test.espresso.Espresso.*;
import static android.support.test.espresso.action.ViewActions.*;
import static android.support.test.espresso.matcher.ViewMatchers.*;
import static android.support.test.espresso.assertion.ViewAssertions.*;

2.关于android.support 包和androidx包。请注意区分它们的差异:difference-between-androidx-and-com-android-support。我这里用的还是android.support包,用androidx的自行修改相关包名。

3.你可能在其他地方见到not()、allOf()等方法,这些也是静态方法,位于类 org.hamcrest.Matchers中,导入即可。

基本使用

1.请自行添加相关注解@RunWith(AndroidJUnit4.class)和@Test等,并且请添加以下成员到测试类中

@Rule
public ActivityTestRule<MainActivity> mActivityRule = new ActivityTestRule<>(MainActivity.class);

这样是为了获取Activity使得测试可进行。此后你可以mActivityRule.getActivity()来获取Activity,这个和实际的Activity没有什么差异,你甚至可以直接调用Activity.findViewById()来获取相关控件以操作。但实际上不推荐,除非有些问题只用单元测试的确难以解决。

2.withId(R.id.xxx),根据id返回一个ViewMatchers,代表着该控件。
withText("admin"),根据文本内容返回一个ViewMatchers,代表着该控件。

3.onView(withId(R.id.xxx)),返回一个ViewInteraction,这样你就可以与该对象代表的控件进行点击、输入文本等交互

4.onView(withId(R.id.xxx)).perform(click());模拟点击该控件

5.onView(withId(R.id.xxx)).perform(typeText("admin"));模拟逐个字母来输入文本“admin”。注意,他是逐个字母来输入,因此中文是无法输入的,请使用replaceText("中文")代替。replaceText是直接替换文本进去,如果你需要类似typeText的来输入中文,请结合Thread.sleep()来设置延时以达到目的。

6.onView(withId(R.id.xxx)).check(matches(isDisplayed()));
isDisplayed()返回一个ViewMatchers,matches()返回一个ViewAssertions,最后check()检测是否符合断言。
简单来讲就是检测withId(R.id.xxx)isDisplayed()是否是等价的ViewMatchers。注意用词是等价不是相等,这里如果该控件可见,将返回真,否则返回假。类似地,onView(withId(R.id.xxx)).check(matches(withText("admin")))可以判断对应控件内容是否是“admin”

AdapterView控件的单元测试

以ListView举例。模拟点击ListView的第0项:

onData(is(instanceOf(String.class)))
        .inAdapterView(withId(R.id.list_view))
        .atPosition(0)
        .perform(click());

英文好的话上面的不难理解。唯一较大差异的就在"String.class"这里,这里只要填上
方法“getItem(int position)”返回的对象的类型即可。如果你用的是SimpleAdapter或其他,请自行弄明白其getItem将返回什么类型的对象。

当然,方法是多样的,也可以更加简单的:

onData(anything())
        .inAdapterView(withId(R.id.list_view))
        .atPosition(0)
        .perform(click());

RecycleView 和 NavigationView

这些较新的android.support(androidx)包的控件,google提供了更方便的方法,只要导入一个包即可:androidTestImplementation group: 'com.android.support.test.espresso', name: 'espresso-contrib', version: '3.0.0'
具体使用很简单,直接用对应类的静态方法就行:

onView(withId(R.id.recycler_view))
        .perform(RecyclerViewActions.actionOnItemAtPosition(0, click()));
onView(withId(R.id.navigation_view))
        .perform(NavigationViewActions.navigateTo(R.id.menu_item));

注意一些坑

1.check(matches(not(isDisplayed())))check(doesNotExist())有一定出入。
官方原话:If the view is gone from the view hierarchy—which can happen when an action caused a transition to another activity—you should use ViewAssertions.doesNotExist()。
如果你只是把控件设置成INVISIBLE,第一种方法是可以的。但是,如果你是把该控件直接从其父控件中removeView()掉,使得其不在当前activity的视图层次,那么你应该使用第二种方法来断言其不出现。

上一篇下一篇

猜你喜欢

热点阅读