Android测试初探(四) Android JUnit Test Runner与Espresso框架

指定Test Runner为Android JUnit Test Runner

前面介绍的JUnit3测试和Android基本测试,都基于Instrumentation Test Runner,之后谷歌又推出了Android JUnit Test Runner。根据官方的资料,前者只支持JUnit3,而后者还可以支持JUnit4。
http://developer.android.com/intl/zh-cn/tools/testing-support-library/index.html

在app模块的build.gradle中,可以指定Test Runner为Android JUnit Test Runner,写法如下。在dependencies中,testCompile表示JUnit测试时编译,androidTestCompile则表示Android Instrumentation Test时编译。

  1. android {

  2. defaultConfig {

  3. // ...

  4. testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

  5. }

  6. packagingOptions {

  7. exclude 'LICENSE.txt'

  8. }

  9. }

  10. dependencies {

  11. androidTestCompile 'com.android.support.test:runner:0.2'

  12. }

配置完成后,即可运行JUnit4测试。除了代码格式不同,其他操作和前面的JUnit3一样,不再重复。

测试设备上的Test Runner

运行测试后,执行adb指令

  1. adb shell pm list instrumentation

可以看到所有Test Runner

  1. instrumentation:com.example.android.apis/.app.LocalSampleInstrumentation (target=com.example.android.apis)
  2. instrumentation:com.jzj1993.unittest.test/android.support.test.runner.AndroidJUnitRunner (target=com.jzj1993.unittest)

还是在设置的所有应用中,可以看到Android JUnit Test Runner,其名称为Test-api,如图。

基于Espresso的测试

Android的测试,除了直接调用相关代码,还有个很常见的途径,就是通过交互界面来测试。如果直接用Instrumentation发送点击等事件来测试,实在非常麻烦。这时可以利用谷歌官方推出的开源框架Espresso做测试。

这里举一个最简单的带UI交互的测试例子。

配置espresso

Espresso基于AndroidJUnitRunner,因此需要进行设置;AndroidJUnitRunner中如果需要获取Activity实例,还需要TestRules包的支持。在build.gradle中包含AndroidJUnitRunner、TestRules和Espresso的完整配置如下。

  1. apply plugin: 'com.android.application'

  2. android {

  3. defaultConfig {

  4. // ...

  5. testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

  6. }

  7. packagingOptions {

  8. exclude 'LICENSE.txt'

  9. }

  10. }

  11. dependencies {

  12. // ...

  13. // 如果依赖项中有appcompat,尽量使用22.0.0,或解决依赖冲突

  14. compile 'com.android.support:appcompat-v7:22.0.0'

  15. androidTestCompile 'com.android.support.test:runner:0.2'

  16. androidTestCompile 'com.android.support.test:rules:0.2'

  17. androidTestCompile 'com.android.support.test.espresso:espresso-core:2.1'

  18. }

依赖冲突解决

注意,在一个稍复杂的项目中配置Espresso,很容易产生依赖项冲突。对于espresso-core:2.1,如果项目的依赖项中有appcompat-v7,尽可能将其版本改为22.0.0。也可以参考下文尝试解决。

《Gradle依赖项学习总结,dependencies、transitive、force、exclude的使用与依赖冲突解决》
http://wiki.sankuai.com/pages/viewpage.action?pageId=404573094

主要的两个冲突项是hamcrest-core和support-annotations,以下是我实际应用到一个项目中的gradle依赖项配置,可供参考。每个包的版本改动,都有可能导致冲突或不兼容。

  1. configurations.all {

  2. resolutionStrategy {

  3. force 'org.hamcrest:hamcrest-core:1.1'

  4. force 'com.android.support:support-annotations:22.0.0'

  5. }

  6. }

  7. dependencies {

  8. // ...

  9. androidTestCompile 'com.android.support.test:runner:0.2'

  10. androidTestCompile 'com.android.support.test:rules:0.2'

  11. androidTestCompile 'com.android.support.test.espresso:espresso-core:2.1'

  12. }

创建待测试项目

这里创建一个TextView和一个Button,点击Button时TextView中的文字改变。activity_main.xml文件如下:

  1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

  2. android:layout_width="match_parent"

  3. android:layout_height="match_parent"

  4. android:gravity="center_horizontal"

  5. android:orientation="vertical">

  6. <TextView

  7. android:id="@+id/text"

  8. android:layout_width="wrap_content"

  9. android:layout_height="wrap_content"

  10. android:text="Text"

  11. android:textSize="25sp" />

  12. <Button

  13. android:id="@+id/button"

  14. android:layout_width="wrap_content"

  15. android:layout_height="wrap_content"

  16. android:text="Button" />

  17. </LinearLayout>

对应的MainActivity如下:

  1. public class MainActivity extends Activity {

  2. @Override

  3. protected void onCreate(Bundle savedInstanceState) {

  4. super.onCreate(savedInstanceState);

  5. setContentView(R.layout.activity_main);

  6. final TextView textView = (TextView) findViewById(R.id.text);

  7. Button button = (Button) findViewById(R.id.button);

  8. button.setOnClickListener(new View.OnClickListener() {

  9. @Override

  10. public void onClick(View v) {

  11. textView.setText("Hello Android Test!");

  12. }

  13. });

  14. }

  15. }

创建测试类

在androidTest/java目录下创建一个MainActivityTest类。测试类中有一个TestCase,其作用是点击R.id.button控件,然后检测文本框是否展示了预期的文本。

  1. package com.jzj1993.unittest;

  2. import android.support.test.rule.ActivityTestRule;

  3. import android.support.test.runner.AndroidJUnit4;

  4. import android.test.suitebuilder.annotation.LargeTest;

  5. import org.junit.Rule;

  6. import org.junit.Test;

  7. import org.junit.runner.RunWith;

  8. import static android.support.test.espresso.Espresso.onView;

  9. import static android.support.test.espresso.action.ViewActions.click;

  10. import static android.support.test.espresso.assertion.ViewAssertions.matches;

  11. import static android.support.test.espresso.matcher.ViewMatchers.withId;

  12. import static android.support.test.espresso.matcher.ViewMatchers.withText;

  13. @RunWith(AndroidJUnit4.class)

  14. @LargeTest

  15. public class MainActivityTest {

  16. @Rule

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

  18. @Test

  19. public void sayHello() throws Exception {

  20. onView(withId(R.id.button)).perform(click());

  21. onView(withId(R.id.text)).check(matches(withText("Hello Android Test!")));

  22. Thread.sleep(5000);

  23. }

  24. }

运行测试

运行测试的方法和前面一样。运行时,可以在Android设备上看到测试效果,按钮被点击,于是文本框展示指定的文本。延时5s测试执行完成后,App退出。Run和Event Log窗口中提示测试通过。

测试失败的情况

修改测试方法的文本,测试就会失败。

  1. @Test
  2. public void sayHello() {
  3. onView(withId(R.id.button)).perform(click());
  4. onView(withId(R.id.text)).check(matches(withText("Test Failed!")));
  5. }