杭州电子商务研究院

使用 Espresso 在 Android 中进行测试 - 第 1 部分(设置和基础知识)

2018-07-23 08:00飞浪

设置和基础知识

应用程序开发生命周期中的一个重要阶段是测试,但问题是为什么?

答案是消除应用程序逻辑中ANR 对话框或意外行为的发生。对于 Android,所有 UI 更新都由主线程处理,如果主线程无法在 5 秒内响应用户事件,则会导致应用程序崩溃,并为用户提供关闭应用程序的选项,而不是让用户处于无限等待状态,从而导致糟糕的用户体验。

“应用程序无响应”对话框是主线程(又称 UI 线程)阻塞时间过长时最不希望发生的事件。可能导致此情况的一些情况包括:

  • 渲染更大的图像

  • 在主线程上执行 REST 调用

  • 或者执行任何长时间的计算

其他原因包括异常、无限递归方法调用、缺少类定义、死锁或在旧版本上实现不受支持的功能(如矢量可绘制等)。

测试驱动开发

TDD是一个编写代码、测试和重构代码的递归循环,用于在错误出现时立即解决它们。TDD 列出了在每次修改代码库后测试应用程序运行的方法。TDD 的目标是:

  • 满足指导应用程序设计和开发的要求

  • 为了确定应用程序在不同环境下的性能,请考虑

    • 网络连接

    • 用户输入

    • 服务器稳定性

    • 电池、CPU 和内存优化(内存泄漏)

  • 验证应用程序安全性,防止 SQL 注入和数据安全漏洞

  • 验证特定应用程序 API 的运行情况

“测试导致失败,失败导致理解:- Burt Rutan”

单元和仪器测试

单元测试:单元测试在本地机器上运行,即 Java 虚拟机 (JVM),这意味着它不需要设备或模拟器。单元测试往往很快,因为单元测试不测试任何 Android 特定的依赖项 (Activity、SharedPreferenc) 或使用模拟对象来模仿 Android 框架依赖项的行为。

单元测试位于package-name/src/test/java/下

仪器测试:仪器测试专门用于测试应用程序的 UI,需要使用模拟器或物理设备来执行测试。Android UI 组件依赖Context来访问应用程序中的资源,如 xml、图像、字符串和其他资源。Context 由 Android 操作系统提供,因此对于仪器测试,上下文由Instrumentation API 提供,用于跟踪 Android 操作系统与应用程序之间的交互,即向活动生命周期、环境详细信息等发出命令。

仪器测试位于package-name/src/androidTest/java/下

测试术语

  • AndroidJUnitRunner:AndroidJUnitRunner是一个 JUnit 测试运行器,可让您在 Android 设备上运行 JUnit 测试,同时提供注释以简化测试过程。对于仪器测试,测试类必须以 @RunWith(AndroidJUnit4.class) 为前缀。
  • 注解:@Test注解用于标记要测试的方法,带有@Before注解的方法将首先执行以设置测试环境,带有@After注解的方法将在最后执行。@LargeTest注解用于指示测试持续时间可以大于 1 秒。
  • Espresso:它是一个 Android 测试框架,用于编写用户界面交互的测试。
  • Mockito:Mockito经常用于单元测试,创建虚拟对象来模仿对象的行为。

使用 Espresso 进行仪器测试

Espresso 是 Google 为 UI(用户界面)测试开发的测试框架,其中包括按钮、列表、片段等所有视图组件。Espresso 是测试 API 的集合,专为仪表/UI 测试而设计。UI 测试可确保用户不会遇到不良交互或意外行为。

Espresso 有各种选项可在屏幕上查找“视图”、执行操作以及验证“视图”的可见性和状态/内容。

      onView(withId(R.id.my_view))       // onView() is a ViewMatcher</strong>
  .perform(click())              // click()  is a ViewAction</strong>
  .check(matches(isDisplayed()));// matches(isDisplayed()) is a ViewAssertion
    
  1. ViewMatcher :使用withId(R.id.id_of_view)、withText("通过视图上的文本查找")在 UI 层次结构(xml 布局组件的树结构)中定位视图。

  2. ViewActions :用于使用ViewInteraction.perform(click(),doubleClick())或click()、longClick()、doubleClick () 、 swipeDown()、 swipeLeft() 、swipeRight ()、swipeUp()、typeText()、pressKey()、clearText()等在 UI 视图中执行特定操作或一组操作。

  3. ViewAssertion :使用ViewInteraction.check(断言方法)断言视图的状态,其中断言方法可以是isDisplayed()、isEnabled()、isRoot()。

设置

  • 在build.gradle应用模块中添加 espresso 依赖项:
      dependencies {
  implementation fileTree(dir: 'libs', include: ['*.jar'])

  // AndroidJUnitRunner and JUnit Rules
  androidTestCompile 'com.android.support.test:runner:1.0.1'
  androidTestCompile 'com.android.support.test:rules:1.0.1'

  // espresso support
  androidTestImplementation('com.android.support.test.espresso:espresso-core:3.0.1', {
    exclude group: 'com.android.support', module: 'support-annotations'
  })
  androidTestCompile 'com.android.support.test.espresso:espresso-contrib:3.0.1'

  // for intent mocking
  androidTestCompile 'com.android.support.test.espresso:espresso-intents:3.0.1'

  // for network testing to track idle state
  androidTestCompile 'com.android.support.test.espresso.idling:idling-concurrent:3.0.1'
  androidTestCompile 'com.android.support.test.espresso:espresso-idling-resource:3.0.1'

  // other dependencies
}
    
  • 要启用仪器测试,请添加以下依赖项:
      testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    

完整的build.gradle(应用程序)文件:

      apply plugin: 'com.android.application'

android {
  compileSdkVersion 26
  buildToolsVersion "26.0.1"

  defaultConfig {
    applicationId "com.pavneet_singh.espressotestingdemo"
    minSdkVersion 15
    targetSdkVersion 26
    versionCode 1
    versionName "1.0"
    testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
  }

  buildTypes {
    release {
      minifyEnabled false
      proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
    }
  }
}
dependencies {
    //as mentioned above
}
    

由于未来的更新,您项目中的库版本的值可能会有所不同,其余配置值取决于项目。

关闭默认设备动画

默认动画可能会在 Espresso 测试过程中出现问题,因此建议在测试前关闭设备和模拟器上的动画。

  • 打开“设置”

  • 选择开发者选项(如果未激活,则在“关于设备设置”中多次单击版本/编号)

  • 禁用:

    1. 窗口动画缩放

    2. 过渡动画比例

    3. 动画师时长刻度

第一次使用按钮和 EditText 进行 Espresso 测试

MainActivity包含 Button 和 EdiTtext,我们将对其执行测试以验证可见性、输入的值和点击操作。

使用以下代码设置 MainActivity

MainActivity的activity_main.xml布局源代码

      <?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.pavneet_singh.MainActivity">

    <EditText
        android:layout_marginTop="30dp"
        android:id="@+id/editTextName"
        android:hint="Enter Name"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:ems="10"
        android:inputType="textPersonName" />

    <Button
        android:onClick="setDefaultText"
        android:layout_marginTop="60dp"
        android:id="@+id/button"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Clear" />

    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|end"
        android:layout_margin="@dimen/fab_margin"
        app:srcCompat="@android:drawable/ic_dialog_email" />
</LinearLayout>
    

xml中的onClick属性指向MainActivity中一个处理点击事件的公共方法,如下所示:

      package com.pavneet_singh;
import android.content.Intent;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.widget.EditText;
import com.pavneet_singh.espressotestingdemo.FruitListActivity;
import com.pavneet_singh.espressotestingdemo.R;

public class MainActivity extends AppCompatActivity {
  private EditText editTextName;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    setContentView(R.layout.activity_main);

    editTextName = (EditText)findViewById(R.id.editTextName);

    FloatingActionButton fab = (FloatingActionButton) findViewBy
曝光36435浏览1892