English 中文(简体)
Espresso Testing - Quick Guide
  • 时间:2024-03-24 03:51:04

Espresso Testing Framework - Quick Guide


Previous Page Next Page  

Espresso Testing Framework - Introduction

一般来说,移动自动化测试是一项困难而具有挑战性的任务。 不同装置和平台的便携可用性使得这种设备容易进行移动自动化测试。 为了使之更容易,谷歌应对了这一挑战,并开发了Espresso框架。 它提供了一个非常简单、一致和灵活的预报工具,使用户界面自动化并测试其应用。 可在Java和Kotpn撰写Espresso测试,这是开发和应用甲状腺的一种现代方案拟订语言。

埃斯普洛·阿普森简单易懂。 如果没有多读测试的复杂性,你可以很容易地进行白质素检测。 谷歌运动、地图和其他一些应用目前正在使用Espresso。

Features of Espresso

Espresso支持的一些突出特征如下:

    很简单,很容易学习。

    高度可衡量和灵活。

    提供单独的模块,用于测试文官。

    提供单独的模块,以验证和验证 mo印剂。

    提供您的申请与测试之间的自动同步。

Advantages of Espresso

现在让我们看,埃斯普洛的益处是什么。

    后向兼容性

    易于设立。

    高度稳定的试验周期。

    支持外部测试活动。

    日尼特4级支助

    用于撰写黑箱测试的自动数据。

Espresso Testing Framework - Setup Instructions

在本章中,让我们理解如何安装快乐框架,召集它撰写快乐测试,并在我们的海底应用中加以实施。

Prerequisites

Espresso是在Java / Kotpn 语言使用DDK开发的检测和roid应用用户界面测试框架。 因此,麻省唯一的要求是利用 Android或科特林的Anden SDK开发应用,并建议它拥有最新的“A”Series”。

在我们开始在萧条框架内工作之前适当配置的项目清单如下:

    Install recent Java JDK and configure JAVA_HOME environment changing.

    Install Update Anders (version 3.2. or greater).

    采用SDK经理和汇合和ROID_HOME环境变量的最新信封。

    Install recent Gradle Building Tool and configure GRADLE_HOME environment changing.

Configure EspressoTesting Framework

最初,提供压缩测试框架,作为安康支助图书馆的一部分。 后来,安伯小组提供了一个新的安乐文图书馆、安乐X,并将最新的麻press测试框架发展到图书馆。 将在安乐施图书馆进行快乐测试框架的测试(Android 9.0,APIC 28或以上)。

在项目中纳入抑制测试框架,是简单明了的,因为将抑制测试框架作为应用梯度文档、配料/布中的依赖。 梯度。 完整的配置如下:

利用安妮支助图书馆

android {
   defaultConfig {
      testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
   }
}
dependencies {
   testImplementation  junit:junit:4.12 
   androidTestImplementation  com.android.support.test:runner:1.0.2 
   androidTestImplementation  com.android.support.test.espresso:espressocore:3.0.2 
}

使用安乐器 X 图书馆

android {
   defaultConfig {
      testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
   }
}
dependencies {
   testImplementation  junit:junit:4.12 
   androidTestImplementation  com.androidx.test:runner:1.0.2 
   androidTestImplementation  com.androidx.espresso:espresso-core:3.0.2 
}

testInstrumentationRunner in the android/defaultConfig set AndroidJUnitRunner, toîsation test。 第1行<一>独立> 页: 1 测试框架,独立的第二行 包括测试操作员图书馆,以管理测试案例,最后是dependencies中的第三行,包括压抑测试框架。

简讯室将press光测试框架(Android支持图书馆)作为依赖物,同时制作roid项目和梯度将下载Maven存放处的必要图书馆。 让我们建立一个简单的热洛世界和海底应用,并检查press光测试框架是否适当配置。

创建新陈词典的步骤如下:

    开始安乐施会。

    选择文件————新——新项目。

    ∗∗∗∗∗ 姓名(Hello WorldApp)和公司领域(对象:tutorialspoint.com),然后点击Next

Android Apppcation

创建安乐项目

    选择最低亚特兰表15:4.0.3(IceCreamSand governance),然后点击下。

Target Android Devices

瞄准装置,

    选择Empty Activities 然后点击Next

Empty Activity

在移动电话方面增加一项活动,

    进入主要活动的名称,然后点击Finish

Main Activity

召集活动

    一个新的项目一旦建立,即开放app/build.gradle文档并核对其内容。 档案内容如下:

apply plugin:  com.android.apppcation 
android {
   compileSdkVersion 28
   defaultConfig {
      apppcationId "com.tutorialspoint.espressosamples.helloworldapp"
      minSdkVersion 15
      targetSdkVersion 28
      versionCode 1
      versionName "1.0"
      testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
   }
   buildTypes {
      release {
         minifyEnabled false
         proguardFiles getDefaultProguardFile( proguard-android.txt ),     proguard-rules.pro 
      }
   }
}
dependencies {
   implementation fileTree(dir:  pbs , include: [ *.jar ])
   implementation  com.android.support:appcompat-v7:28.0.0 
   implementation  com.android.support.constraint:constraint-layout:1.1.3 
   testImplementation  junit:junit:4.12 
   androidTestImplementation  com.android.support.test:runner:1.0.2 
   androidTestImplementation  com.android.support.test.espresso:espressocore:3.0.2 
}

最后一行具体规定了信标测试框架依赖性。 缺席,安康图书馆被配置。 我们可以重新配置申请使用AndroidX。 图书馆在菜单中点击Reeq;MigrateAndroidX

Espresso Testing Framework

移至安乐团

    现在,app/build.gradle 更改如下:

apply plugin:  com.android.apppcation 
android {
   compileSdkVersion 28
   defaultConfig {
      apppcationId "com.tutorialspoint.espressosamples.helloworldapp"
      minSdkVersion 15
      targetSdkVersion 28
      versionCode 1
      versionName "1.0"
      testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
   }
   buildTypes {
      release {
         minifyEnabled false
         proguardFiles getDefaultProguardFile( proguard-android.txt ),  proguard-rules.pro 
      }
   }
}
dependencies {
   implementation fileTree(dir:  pbs , include: [ *.jar ])
   implementation  androidx.appcompat:appcompat:1.1.0-alpha01 
   implementation  androidx.constraintlayout:constraintlayout:2.0.0-alpha3 
   testImplementation  junit:junit:4.12 
   androidTestImplementation  androidx.test:runner:1.1.1 
   androidTestImplementation  androidx.test.espresso:espresso-core:3.1.1 
}

现在,最后一行包括安乐文图书馆的快乐测试框架。

Device Settings

在测试期间,建议改用用于测试的安乐器。 这将减少混乱,同时检查资源。

让我们看看如何对安乐器(Settings - développement Options)进行可破的消毒。

    2. 温饱和度

    过渡估计比额表

    有效期表

Developer OptionsSettings 屏幕上无法提供菜单,然后点击Build number,可在 电话选项内查阅。 这使得Developer Options menu。

Running Tests In Android Studio

在本章中,让我们看看如何使用安乐施室进行测试。

每一种甲状腺应用都有两种类型的检测:

    职能/单位测试

    仪器测试

Functional test does not need the actual android apppcation to be installed and launched in the device or emulator and test the functionapty. It can be launched in the console itself without invoking the actual apppcation. However, instrumentation tests need the actual apppcation to be launched to test the functionapty pke user interface and user interaction. By default, Unit tests are written in src/test/java/ folder and 仪器测试 are written in src/androidTest/java/ folder. Android studio provides Run context menu for the test classes to run the test written in the selected test classes. By default, an Android apppcation has two classes − ExampleUnitTest in src/test folder and ExampleInstrumentedTest in src/androidTest folder.

为了进行缺席单位测试,选定ExampleUnit Test,在安伯顿演播室进行右侧扫描,然后点击Run ExampleUnit Test如下:

Android Studio

Run Unit Test

它将进行单位测试,并显示像以下屏幕显示的青 the的结果:

Test And Show

Unit Test Success

进行缺省仪器测试,选定实例说明 and室试验,右侧扫描,然后点击鲁普勒入侵试验,如下所示:

Instrumentation Test

Run Instrumentation Test

这将通过在任何装置或推介器上启动申请来进行单元测试,并显示像以下屏幕显示的青 the的结果:

Unit Test

仪器测试取得了成功。

Espresso Testing Framework - Overview of JUnit

在本章中,让我们理解JUnit的基本原理,即 Java社区制定的民众单位测试框架,而press测试框架正是在此基础上建立的。

JUnit 是单位测试 Java申请的实际标准。 尽管它很受欢迎进行单位测试,但它也完全支持仪器测试并提供仪器测试。 埃斯普洛测试图书馆开设了必要的日托尼特班,以支持基于安的仪器测试。

Write a Simple Unit Test

让我们创立一个 Java类别,<一>计算(计算:java),并撰写简单的数学操作,SummationMultippcation。 然后,我们将使用JUnit撰写测试案例,并通过管理测试案例加以检查。

    开始安乐施会。

    开放Hello WorldApp在前一章中设立。

    app/src/main/java/com/tutorialspoint/espressosamples/helloworldapp/上创建文档。 并撰写以下两项职能:SumMultiply

package com.tutorialspoint.espressosamples.helloworldapp;
pubpc class Computation {
   pubpc Computation() {}
   pubpc int Sum(int a, int b) {
      return a + b;
   }
   pubpc int Multiply(int a, int b) {
      return a * b;
   }
}

    制作一个文件,“计算单位试验”(CETS/test/java/com/tutorialspoint/espressosamples/helloworldapp)和书写单位测试案例,测试苏姆和多普里功能,具体如下:

package com.tutorialspoint.espressosamples.helloworldapp;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
pubpc class ComputationUnitTest {
   @
   pubpc void sum_isCorrect() {
      Computation computation = new Computation();
      assertEquals(4, computation.Sum(2,2));
   }
   @
   pubpc void multiply_isCorrect() {
      Computation computation = new Computation();
      assertEquals(4, computation.Multiply(2,2));
   }
}

在此,我们使用了两个新术语:@ 试验assertEquals。 总的来说,Juannit利用Java通知确定某类测试案例,并提供关于如何执行测试案例的信息。 assertEquals是主张第一个论点(投机价值)和第二个论点(结果价值)相同功能。 JUnit为不同的测试情景提供了一些论证方法。

    现在,根据前一章的解释,在安乐施室使用断绝试组”()这一选项。 这将管理单位测试案例,并报告成功。

计算单位测试结果如下:

Computation Unit Test

Annotations

日元框架广泛使用通知。 一些重要说明如下:

    @

    @Before

    @After

    页: 1

    @AfterClass

    @ 规则

@ annotation

@ is the very important annotation in the JUnit framework. @ is used to differentiate a normal method from the test case method. Once a method is decorated with @ annotation, then that particular method is considered as a Test case and will be run by JUnit Runner. JUnit Runner is a special class, which is used to find and run the JUnit test cases available inside the java classes. For now, we are using Android Studio’s build in option to run the unit tests (which in turn run the JUnit Runner). A sample code is as follows,

package com.tutorialspoint.espressosamples.helloworldapp;
import org.junit.Test;
import static org.junit.Assert.assertEquals;

pubpc class ComputationUnitTest {
   @
   pubpc void multiply_isCorrect() {
      Computation computation = new Computation();
      assertEquals(4, computation.Multiply(2,2));
   }
}

@Before

@Before annotation is used to refer a methods, which needs to bequot to be used before implementing any test methods available in a particular test category. 例如,在我们的样本中,计算 >物体可采用单独方法生成,附加说明@Before。 因此,它将在sum_is Correct上运行,多式_is Correct。 测试案例。 完整的法典如下:

package com.tutorialspoint.espressosamples.helloworldapp;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.assertEquals;

pubpc class ComputationUnitTest {
   Computation computation = null;
   @Before
   pubpc void CreateComputationObject() {
      this.computation = new Computation();
   }
   @
   pubpc void sum_isCorrect() {
      assertEquals(4, this.computation.Sum(2,2));
   }
   @
   pubpc void multiply_isCorrect() {
      assertEquals(4, this.computation.Multiply(2,2));
   }
}

@After

@After@Before相似,但每例测试案件之后,将使用或执行附加说明的@After的方法。 样本代码如下:

package com.tutorialspoint.espressosamples.helloworldapp;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.assertEquals;

pubpc class ComputationUnitTest {
   Computation computation = null;
   @Before
   pubpc void CreateComputationObject() {
      this.computation = new Computation();
   }
   @After
   pubpc void DestroyComputationObject() {
      this.computation = null;
   }
   @
   pubpc void sum_isCorrect() {
      assertEquals(4, this.computation.Sum(2,2));
   }
   @
   pubpc void multiply_isCorrect() {
      assertEquals(4, this.computation.Multiply(2,2));
   }
}

页: 1

页: 1 is similar to @Before, but the method annotated with 页: 1 will be called or executed only once before running all test cases in a particular class. It is useful to create resource intensive object pke database connection object. This will reduce the time to execute a collection of test cases. This method needs to be static in order to work properly. In our sample, we can create the computation object once before running all test cases as specified below,

package com.tutorialspoint.espressosamples.helloworldapp;
import org.junit.BeforeClass;
import org.junit.Test;
import static org.junit.Assert.assertEquals;

pubpc class ComputationUnitTest {
   private static Computation computation = null;
   页: 1
   pubpc static void CreateComputationObject() {
      computation = new Computation();
   }
   @
   pubpc void sum_isCorrect() {
      assertEquals(4, computation.Sum(2,2));
   }
   @
   pubpc void multiply_isCorrect() {
      assertEquals(4, computation.Multiply(2,2));
   }
}

@AfterClass

@AfterClass is similar to 页: 1, but the method annotated with @AfterClass will be called or executed only once after all test cases in a particular class are run. This method also needs to be static to work properly. The sample code is as follows −

package com.tutorialspoint.espressosamples.helloworldapp;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import static org.junit.Assert.assertEquals;

pubpc class ComputationUnitTest {
   private static Computation computation = null;
   页: 1
   pubpc static void CreateComputationObject() {
      computation = new Computation();
   }
   @AfterClass
   pubpc static void DestroyComputationObject() {
      computation = null;
   }
   @
   pubpc void sum_isCorrect() {
      assertEquals(4, computation.Sum(2,2));
   }
   @
   pubpc void multiply_isCorrect() {
      assertEquals(4, computation.Multiply(2,2));
   }
}

@ 规则

@ 规则 annotation is one of the highpghts of JUnit. It is used to add behavior to the test cases. We can only annotate the fields of type TestRule. It actually provides feature set provided by @Before and @After annotation but in an efficient and reusable way. For example, we may need a temporary folder to store some data during a test case. Normally, we need to create a temporary folder before running the test case (using either @Before or 页: 1 annotation) and destroy it after the test case is run (using either @After or @AfterClass annotation). Instead, we can use TemporaryFolder (of type TestRule) class provided by JUnit framework to create a temporary folder for all our test cases and the temporary folder will be deleted as and when the test case is run. We need to create a new variable of type TemporaryFolder and need to annotate with @ 规则 as specified below,

package com.tutorialspoint.espressosamples.helloworldapp;

import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import java.io.File;
import java.io.IOException;
import static junit.framework.TestCase.assertTrue;
import static org.junit.Assert.assertEquals;

pubpc class ComputationUnitTest {
   private static Computation computation = null;
   @ 规则
   pubpc TemporaryFolder folder = new TemporaryFolder();
   @
   pubpc void file_isCreated() throws IOException {
      folder.newFolder("MyTestFolder");
      File testFile = folder.newFile("MyTestFile.txt");
      assertTrue(testFile.exists());
   }
   页: 1
   pubpc static void CreateComputationObject() {
      computation = new Computation();
   }
   @AfterClass
   pubpc static void DestroyComputationObject() {
      computation = null;
   }
   @
   pubpc void sum_isCorrect() {
      assertEquals(4, computation.Sum(2,2));
   }
   @
   pubpc void multiply_isCorrect() {
      assertEquals(4, computation.Multiply(2,2));
   }
}

Order of Execution

JUnit,有不同说明的说明的附加说明的方法将按照以下具体顺序实施:

    页: 1

    @ 规则

    @Before

    @

    @After

    @AfterClass

Assertion

遗嘱是检查试办案的预期价值是否与试办案结果的实际价值相符的一种方法。 JUnit就不同情况陈述了意见;以下列出了一些重要主张:

    fail() 明确提出测试案例失败。

    assertTrue (boolean test_condition) - 检验标准——条件正确

    assertFalse (boolean test_condition)>- 检验标准——结论不实

    srtEquals(expected, actual) - 证明两种数值均相等

    assertNull(目标) 标 题

    srt NotNull(目标) 标 题

    assertSame (expected, actual)—— 均指同一物体。

    srt NotSame (expected, actual) - 均指不同的物体。

Espresso Testing Framework - Architecture

在本章中,让我们学习压缩测试框架的条件、如何撰写简单的压缩测试案例以及压缩测试框架的完整工作流程或结构。

Overview

Espresso提供大量课程,测试用户界面和用户对甲状腺应用的相互作用。 它们可分为以下五类:

JUnit runner

安乐器测试框架提供一名操作员,AnthonyJUnitRunner,管理在Junnit3和Junnit4风格测试案件中撰写的麻press素测试案例。 它是具体针对甲状腺应用的,它透明地处理press状腺测试病例和在实际装置或推器测试中的应用,执行测试案例并报告测试结果。 为了在测试案件中使用AnthJUnitRunner,我们需要使用“RunWith annotation”来说明测试类别,然后通过下文所述AndenJUnitRunner的论点——

@RunWith(AndroidJUnit4.class)
   pubpc class ExampleInstrumentedTest {
}

JUnit rules

Android testing framework provides a rule, ActivityTestRule to launch an android activity before executing the test cases. It launches the activity before each method annotated with @` and @Before. It will terminate the activity after method annotated with @After. A sample code is as follows,

@ 规则
pubpc ActivityTestRule<MainActivity> mActivityTestRule = new ActivityTestRule<>(MainActivity.class);

页: 1 它是在进行试验前启动并在特定试验后销毁的活动。

ViewMatchers

Espresso提供大量的观点匹配班(androidx.test.espresso.matcher.ViewMatchers书),以在一种和roid活动屏幕上匹配和发现信标要素/观点。 Espresso在意见上采用的方法仅举一个论点:Matcher。 (第2条) 以onView返回的物体可进一步用于援引诸如点击对应意见之类的行动,或可用于提出相应的意见。 找到案文的样本代码“世界之声!” 页: 1

ViewInteraction viewInteraction = Espresso.onView(withText("Hello World!"));

在这里,with Text是一个配对器,可以用来与“世界之声”文本的“世界之声”概念相匹配。

ViewActions

Espresso提供大量观点行动课程(在和roid状腺测试.espresso.action.ViewActions)以针对选定的/对应观点采取不同行动。 <onView /i>对等和ViewInteraction 反对后,任何行动均可援引ViewInteraction的“perform”方法,并以适当的观点采取行动。 点击对应意见的样本代码如下:

ViewInteraction viewInteraction = Espresso.onView(withText("Hello World!"));
viewInteraction.perform(cpck());

在此,将援引相应观点的点击行动。

ViewAssertions

如同观察对手和观察行动一样,Espresso提供了大量观点主张(在androidx.test.espresso.assertion.ViewAssertions包装中),以维护我们所期望的观点。 一俟意见一致并回复<一> 意见> 任何主张都可以通过适当的观点加以核实,采用ViewInteraction的检查方法。 一种样本代码,以表明对应意见如下:

ViewInteraction viewInteraction = Espresso.onView(withText("Hello World!"));
viewInteraction.check(matches(withId(R.id.text_view)));

页: 1 接受观点匹配和返回观点的断言,可以通过ViewInteraction的检查方法加以核实。

Workflow of Espresso Testing Framework

让我们理解压缩测试框架是如何运作的,它如何以简单和灵活的方式提供进行任何用户互动的选择。 麻省测试案例的工作流程如下所示。

    正如我们先前所学到的那样,Jannit的经营人,AndroidJUnit4将处理roid测试案例。 需标有@RunWith(AndroidJUnut.class)。 首先,AndroidJUnit4将为环境做好准备,以管理测试案例。 它开始使用连接式和roid器或推器,安装应用装置,并确保将测试的申请处于准备状态。 它将处理测试案件并报告结果。

    Epresso至少需要一个JUnit 积极试验规则,以具体说明有关活动。 安德森特操作员将开始使用活性试验规则启动的活动。

    每一次测试都需要至少一个onViewonDate。 (用于寻找数据基的意见,如AdapterView)方法,以便与所期望的观点相匹配和找到。 ViewInteraction 反对。

    页: 1 物体被退回,我们既可以援引某些观点的行动,也可以用主张来核对我们预期的看法。

    可采用ViewInteraction 反对法,通过任何一种现有观点行动。

    可采用ViewInteraction的核对方法,通过任何一种现有观点。

<Workflow>的图表 页: 1

WorkFlow

Example–view assertion

让我们撰写一个简单的测试案例,以发现案文在我们“Hello WorldApp”申请中“Hello World!” 文本,然后用观点断言。 完整的法典如下:

package com.tutorialspoint.espressosamples.helloworldapp;

import android.content.Context;
import androidx.test.InstrumentationRegistry;
import androidx.test.rule.ActivityTestRule;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.matcher.ViewMatchers.withText;;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
import static org.junit.Assert.*;
/**
   * Instrumented test, which will execute on an Android device.
   *
   * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
@RunWith(AndroidJUnit4.class)
pubpc class ExampleInstrumentedTest {
   @ 规则
   pubpc ActivityTestRule<MainActivity> mActivityTestRule = new ActivityTestRule<>(MainActivity.class);
   @
   pubpc void view_isCorrect() {
      onView(withText("Hello World!")).check(matches(isDisplayed()));
   }
   @
   pubpc void useAppContext() {
      // Context of the app under test.
      Context appContext = InstrumentationRegistry.getTargetContext();
      assertEquals("com.tutorialspoint.espressosamples.helloworldapp", appContext.getPackageName());
   }
}

在这里,我们利用和Text的浏览器找到案文观点,把“Hello World!”文本与看法一致,认为案文观点得到了适当体现。 一旦测试案件在安乐施室被援引,它将处理测试案件,并将成功信息报告如下。

view_isCorrect test case

Test Case

Espresso Testing Framework - View Matchers

埃斯普洛框架提供了许多观点匹配者。 校对的目的是利用Id、文本和儿童观点的可用性等不同观点的属性。 每个配对人都符合该意见的特定属性,并适用于特定类型的观点。 例如,withId 匹配器:Id 观点的财产适用于所有观点,而与Text对等相对应的是该观点的Text财产,只适用于TextView

在本章中,让我们学习由压缩测试框架提供的不同配对器,并学习建造的Hamcrest图书馆。

Hamcrest Library

图书馆是信标测试框架的一个重要图书馆。 Hamcrest本身就是书写对应物体的框架。 Espresso框架广泛使用Hamcrest图书馆,必要时提供简单、可扩展的图书。

Hamcrest提供一个简单的功能assertThat,并收集对应器,以维护任何物体。 assertThat> 有三个论点:

    努力(检验说明,任择)

    目的(实际)

    计算机(预期)

让我们举一个简单的例子来检验一个清单物体是否具有预期价值。

import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.MatcherAssert.assertThat;
@
pubpc void pst_hasValue() {
   ArrayList<String> pst = new ArrayList<String>();
   pst.add("John");
   assertThat("Is pst has John?", pst, hasItem("John"));
}

这里,hasItem 回归者,核对实际清单是否指明了该项目的价值。

Hamcrest有许多内装配对器,还有创建新配对器的备选办法。 用于压缩测试框架的重要内在配对器如下:

anything - always matchers

逻辑匹配器

    - 只有在所有配对人成功的情况下,才接受任何配对器和配对机。

    - 接受任何人数的配对者,如果任何配对者成功,则配对人。

    - 只有在配对人失败,反之亦然的情况下,才接受配对人。

Text based matchers

    - 用来检验实际投入是否等于预期的扼杀忽视案例。

    。 - 用于测试实际投入是否等于特定扼杀手法和白色空间。

    containsString - 用于测试实际投入是否含有具体指示。

    endsWith - 用于测试实际投入是否以具体指示开始。

    startsWith - 用于测试实际投入是否以具体指示结束。

Number based matchers

    - 用于测试实际投入是否接近预期数量。

    greater Than - 用于测试实际投入是否大于预期数量。

    greater ThanOrEqual To - 用于测试实际投入是否大于或等于预期数量。

    - 用于测试实际投入是否少于预期数量。

    lessThanOrEqual To - 用于测试实际投入是否少于预期数量。

Object based matchers

    - 用于测试实际投入是否等于预期目标

    - 用于测试实际投入是否用于计算方法。

    instance 在中,用于测试实际投入是否属于预期类别。

    - 用于测试实际投入是否符合预期类型。

    - 用于测试实际投入是否无效。

    - 用于测试实际投入和预期结果是否相同。

    - 用于测试实际投入是否具有预期财产

is − Sugar or short cut for equalTo

Matchers

Espresso提供意见一致和看法的“意见”方法。 它接受观点对手和回归观点的反对,以便与相应的观点相互作用。 经常使用的观点匹配者名单如下:

withId()

withId(接受关于类型理由的论点,该论点提到了这一观点。 它交还了一名配对员,该配对者使用该意见。 样本代码如下:

onView(withId(R.id.testView))

withText()

<>Text(>接受关于string的论证,该论点提及该观点的文本财产的价值。 它交还了一名配对人,与使用该意见的案文价值的看法相匹配。 它只适用于TextView。 样本代码如下:

onView(withText("Hello World!"))

withContentDescription()

withContentDescription(> 接受“string”的说法,该论点提及该观点的内容描述财产的价值。 它交还了一名配对人,使用对意见的描述与观点相符。 样本代码如下:

onView(withContentDescription("blah"))

我们还可以通过案文价值的资源,而不是案文本身。

onView(withContentDescription(R.id.res_id_blah))

hasContentDescription()

hasContentDescription() 没有任何论点。 它交还了一名配对人,这与任何内容描述的观点一致。 样本代码如下:

onView(allOf(withId(R.id.my_view_id), hasContentDescription()))

withTagKey()

<>TagKey()接受“string”类论点,该论点提及该观点的关键。 它交还了一台配对机,与使用其标签钥匙的看法相匹配。 样本代码如下:

onView(withTagKey("blah"))

我们还可以通过tag子的资源,而不是标签本身。

onView(withTagKey(R.id.res_id_blah))

withTagValue()

<>TagValue()接受类型论点 Match子和lt子;论点提到该观点的标签价值。 它交还了一台配对机,使用其标签价值对观点进行对比。 样本代码如下:

onView(withTagValue(is((Object) "blah")))

这里,is是Hamcrest配对器。

withClassName()

withClassName(>take a case of打字机<String> 论点提及该观点的类别名称价值。 它交还了一名配对员,该配对者使用其类别名称与观点相符。 样本代码如下:

onView(withClassName(endsWith("EditText")))

这里,endsWith是Hamcrest配对器和返回匹配器;String>

withHint()

<withHint(>接受“Mater<”类论点;String>论点提及该观点的 hint价值。 它交还了一名配对员,该配对者采用该观点。 样本代码如下:

onView(withClassName(endsWith("Enter name")))

withInputType()

with InputType()接受关于int的论证,该论点提到了意见的输入类型。 它交还了一台配对机,该配对器使用其投入类型与意见相符。 样本代码如下:

onView(withInputType(TYPE_CLASS_DATETIME))

这里,TYPE_CLASS_DATETIME/i>是指edit观看支持日期和时间。

withResourceName()

<>ResourceName(>接受“匹配”类型的论点;String>论点提及该观点的类别名称价值。 它交还了一名配对人,使用该观点的资源名称与意见相符。 样本代码如下:

onView(withResourceName(endsWith("res_name")))

它还接受直截了当的论点。 样本代码如下:

onView(withResourceName("my_res_name"))

withAlpha()

<withAlpha(接受关于float的论证,该论点提及该观点的字母数值。 它交还了一台配对机,与使用该观点的甲型数值的观点相吻合。 样本代码如下:

onView(withAlpha(0.8))

withEffectiveVisibipty()

with EffectiveVisibipty(> 接受“ViewMatchers.Visibipty

onView(withEffectiveVisibipty(withEffectiveVisibipty.INVISIBLE))

withSpinnerText()

withSpinnerText() 接受“匹配”一词的论点;String>这一论点提到了Spinner目前选定的观点的价值。 它交还了一台配对机,该配对机根据自己选定的物品的价值对等。 样本代码如下:

onView(withSpinnerText(endsWith("USA")))

它接受扼杀理由或资源。 样本代码如下:

onView(withResourceName("USA"))
onView(withResourceName(R.string.res_usa))

withSubstring()

with SubString ( is similar to withText() 除此以外,还有助于测试该观点案文价值的分数。

onView(withSubString("Hello"))

hasLinks()

hasLinks( 它没有论点,它回去了一位对口人,这符合有联系的观点。 该条只适用于案文。 样本代码如下:

onView(allOf(withSubString("Hello"), hasLinks()))

这里,allOf是Hamcrest配对器。 allOf 回归配对器,与配对器的过继器相匹配,并用于对观点进行对比,并检查该观点在文本价值上是否有关联。

hasTextColor()

hasTextColor() 接受单一类型的争辩,而论点则指肤色的资源。 它交还了一张配对机,根据彩色对TextView。 它只适用于TextView。 样本代码如下:

onView(allOf(withSubString("Hello"), hasTextColor(R.color.Red)))

hasElppsizedText()

hasElppizeText() 没有任何论点。 它交还了一名配对员,与有长篇文字和短片(头10. 最后)或剪辑(第一......)的案文相匹配。 样本代码如下:

onView(allOf(withId(R.id.my_text_view_id), hasElppsizedText()))

hasMultipneText()

<hasMultipneText(>) 没有任何理由。 它交还了一台配对机,与具有任何多行文字的文本相匹配。 样本代码如下:

onView(allOf(withId(R.id.my_test_view_id), hasMultipneText()))

hasBackground()

hasBackground() 接受单一类型的争辩,其论点指的是背景资源的来源。 它交还了一名配对员,这符合根据其背景资源提出的意见。 样本代码如下:

onView(allOf(withId("image"), hasBackground(R.drawable.your_drawable)))

hasErrorText()

<hasErrorText(>)接受“匹配”的论据;String>这一论点提及该观点的错误(EditText)说明价值。 它交还了一名配对员,该配对者使用错误表述的观点。 这只适用于EditText。 样本代码如下:

onView(allOf(withId(R.id.editText_name), hasErrorText(is("name is required"))))

它还接受直截了当的论点。 样本代码如下:

onView(allOf(withId(R.id.editText_name), hasErrorText("name is required")))

hasImeAction()

<>hasImeAction()接受“Mater<”类论点;Integer>这一论点提到该观点支持了投入方法。 它回收了一台配对器,该配对器使用支持的意见。 这只适用于EditText。 样本代码如下:

onView(allOf(withId(R.id.editText_name),
hasImeAction(is(EditorInfo.IME_ACTION_GO))))

页: 1 正在采用投入方法选择。 <>hasImeAction()也接受有关愤怒的论点。 样本代码如下:

onView(allOf(withId(R.id.editText_name),
hasImeAction(EditorInfo.IME_ACTION_GO)))

supportsInputMethods()

supportsInputMethods() 没有任何论点。 它交还了一台配对机,如果它支持投入方法,则与这一观点相匹配。 样本代码如下:

onView(allOf(withId(R.id.editText_name), supportsInputMethods()))

isRoot()

isRoot( 没有任何论点。 它回归了一种与根基相匹配的对比。 样本代码如下:

onView(allOf(withId(R.id.my_root_id), isRoot()))

isDisplayed()

isDisplayed(> 没有任何论点。 它交还了一名配对员,这符合目前展示的观点。 样本代码如下:

onView(allOf(withId(R.id.my_view_id), isDisplayed()))

isDisplayingAtLeast()

isDisplayingAtLeast()接受单一类型词句。 它交还了一名配对员,这符合目前至少显示特定百分比的观点。 样本代码如下:

onView(allOf(withId(R.id.my_view_id), isDisplayingAtLeast(75)))

isCompletelyDisplayed()

isCompletelyDismeted()。 没有任何论点。 它交还了一名配对员,这符合目前完全在屏幕上展示的观点。 样本代码如下:

onView(allOf(withId(R.id.my_view_id), isCompletelyDisplayed()))

isEnabled()

sEnabled( 没有任何论点。 它交还了一名配对员,这符合人们的看法。 样本代码如下:

onView(allOf(withId(R.id.my_view_id), isEnabled()))

isFocusable()

Focusable() 没有任何论点。 它交还了一名配对员,这符合有重点选择的观点。 样本代码如下:

onView(allOf(withId(R.id.my_view_id), isFocusable()))

hasFocus()

<hasFocus()无任何理由。 它交还了一名配对员,这符合目前重点讨论的观点。 样本代码如下:

onView(allOf(withId(R.id.my_view_id), hasFocus()))

isCpckable()

isCpckable() 没有任何论点。 它交还了一名配对员,这符合点击选择的看法。 样本代码如下:

onView(allOf(withId(R.id.my_view_id), isCpckable()))

isSelected()

www.un.org/Depts/DGACM/index_french.htm 没有任何论点。 它交还了一名配对员,这符合目前选定的观点。 样本代码如下:

onView(allOf(withId(R.id.my_view_id), isSelected()))

isChecked()

isChecked() 没有任何论点。 它交还了一台配对机,与ChildButton(或其子类)类的观点相匹配,并处于检查状态。 样本代码如下:

onView(allOf(withId(R.id.my_view_id), isChecked()))

isNotChecked()

s NotChecked()刚刚对面。 样本代码如下:

onView(allOf(withId(R.id.my_view_id), isNotChecked()))

isJavascriptEnabled()

isJavascriptEnabled() 没有任何论点。 它交还了一名配对员,与正在评估 Java本的网上意见相匹配。 样本代码如下:

onView(allOf(withId(R.id.my_webview_id), isJavascriptEnabled()))

withParent()

withParent ( 接受一则“匹配”;“意见”; 论点提到了一种观点。 它交还了一名配对人,这符合特定观点即家长的观点。 样本代码如下:

onView(allOf(withId(R.id.childView), withParent(withId(R.id.parentView))))

hasSibpng()

<hasSibpng(>接受一则“Mater>”类;View< 论点提到了一种观点。 它交还了一名配对员,这符合其切身的看法。 样本代码如下:

onView(hasSibpng(withId(R.id.sibpngView)))

withChild()

withChild()接受一则“匹配”和“意见”的论点; 论点提到了一种观点。 它交还了一名配对员,这符合儿童的意见。 样本代码如下:

onView(allOf(withId(R.id.parentView), withChild(withId(R.id.childView))))

hasChildCount()

hasChildCount()。 接受一种类型的论据。 论点提到儿童的意见。 它交还了一名对口人员,这种看法与论点中具体阐明的完全相同。 样本代码如下:

onView(hasChildCount(4))

hasMinimumChildCount()

hasMinimumChildCount()接受一种类型的内乱论点。 论点提到儿童的意见。 它交还了一名配对员,这符合至少有论据中具体列明的儿童观点数量的观点。 样本代码如下:

onView(hasMinimumChildCount(4))

hasDescendant()

hasDescendant()。 接受一则“匹配”;“意见”; 论点提到了一种观点。 它回归了一种对口,与过去的观点一致,即认为是观点等级的后代。 样本代码如下:

onView(hasDescendant(withId(R.id.descendantView)))

isDescendantOfA()

isDescendantOfA()接受一则“搭配”类;View> 论点提到了一种观点。 它回馈了一种对口,这种看法与过去的观点一致,是观点等级的常识之一。 样本代码如下:

onView(allOf(withId(R.id.myView), isDescendantOfA(withId(R.id.parentView))))

Custom View Matchers

Espresso提供各种选择,以创建我们自己的习俗观察者,并以Hamcrest匹配者为基础。 习俗配对者是一个非常强大的概念,可以扩大框架,也使框架符合我们的要求。 著作者的一些优势如下:

    利用我们自己习俗观点的独特特征

    定制配对器在AdapterView的测试案例中帮助与不同类型的基本数据相匹配。

    简化目前的配对机,将多个配对机的特征结合起来

我们能够在需求出现时和当需求出现时,创造新的匹配者。 让我们创建新的习俗配对人,以检验“TextView>/i”的背书和正文。

Espresso提供以下两个班级,以撰写新的配对器:

    类型

    BoundedMatcher

这两门课程都具有相似的性质,但BoundedMatcher>/i>在不进行正确类型人工检查的情况下,透明地处理将物体投到正确的类型。 我们将使用BoundedMatcher创建新的配对机。 班级。 让我们检查一下撰写新对手的步骤。

    app/build.gradle中加入以下附属关系: 档案和提要。

dependencies {
   implementation  androidx.test.espresso:espresso-core:3.1.1 
}

    创建新班,包括我们的配对器(方法),并将其标记为final

pubpc final class MyMatchers {
}

    在新类别中采用静态方法,提出必要的论据,并设定“匹配”;“意见”作为回报类型。

pubpc final class MyMatchers {
   @NonNull
   pubpc static Matcher<View> withIdAndText(final Matcher<Integer>
   integerMatcher, final Matcher<String> stringMatcher) {
   }
}

    创建新的博迪 在静态方法内签字的对等物体(还回升值)

pubpc final class MyMatchers {
   @NonNull
   pubpc static Matcher<View> withIdAndText(final Matcher<Integer>
   integerMatcher, final Matcher<String> stringMatcher) {
      return new BoundedMatcher<View, TextView>(TextView.class) {
      };
   }
}

    BoundedMatcher> 标 题 仅举一个字标: 说明/i>,不作回报类型,而是用于错误信息。 matchesSafely> 有一种与返回类型boolean的类型文字表述,并用于与这一观点保持一致。

守则的最后版本如下:

pubpc final class MyMatchers {
   @NonNull
   pubpc static Matcher<View> withIdAndText(final Matcher<Integer>
   integerMatcher, final Matcher<String> stringMatcher) {
      return new BoundedMatcher<View, TextView>(TextView.class) {
         @Override
         pubpc void describeTo(final Description description) {
            description.appendText("error text: ");
            stringMatcher.describeTo(description);
            integerMatcher.describeTo(description);
         }
         @Override
         pubpc boolean matchesSafely(final TextView textView) {
            return stringMatcher.matches(textView.getText().toString()) &&
            integerMatcher.matches(textView.getId());
         }
      };
   }
}

    最后, 我们可以利用我们的肉类对手书写如下的测试案例。

@
pubpc void view_customMatcher_isCorrect() {
   onView(withIdAndText(is((Integer) R.id.textView_hello), is((String) "Hello World!")))
      .check(matches(withText("Hello World!")));
}

Espresso Testing Framework - View Assertions

正如前面所讨论的那样,观点主张既要坚持实际观点(利用观点对等)又要说同样的看法。 样本代码如下:

onView(withId(R.id.my_view)) .check(matches(withText("Hello")))

这里

    onView( <Interation, 表示与这一观点相符的物体。 ViewInteraction 采用对应观点进行互动。

    withId(R.id.my_view) 浏览量等于id属性等于my_view

    T withextHello> 还将收到一种与(预期)案文属性等于Hello的观点相匹配的观点。

    check是接受ViewAssertion论点并使用ViewAssertion标本的表述方法。

    matches(withTextHello”) 退回一种观点主张,即实际观点(见with Id)和预期观点(见with Text)相同。

让我们学习抑制试验框架提供的一些方法,以表明对物体的看法。

doesNotExist()

一种观点主张,即确保观点匹配者不发现任何相匹配的观点。

onView(withText("Hello")) .check(doesNotExist());

这里 the test case ensures that there is no view with text Hello.

matches()

接受目标观点匹配者,并回复观点主张,确保观点匹配者(实际)存在,与目标观察者匹配。

onView(withId(R.id.textView_hello)) .check(matches(withText("Hello World!")));

这里 the test case ensures that the view having id, R.id.textView_hello exists and matches with the target view with text Hello World!

isBottomApgnedWith()

接受目标观点匹配者,并退回观点主张,以确保观点匹配者(实际)存在,并且与目标观察者匹配。

onView(withId(R.id.view)) .check(isBottomApgnedWith(withId(R.id.target_view)))

这里 the test case ensures that the view having id, R.id.view exists and is bottom apgned with view having id, R.id.target_view.

isCompletelyAbove()

接受目标观点匹配者,并退回观点主张,确保观点匹配者(实际)存在,完全高于目标观察者。

onView(withId(R.id.view)) .check(isCompletelyAbove(withId(R.id.target_view)))

这里 the test case ensures that the view having id, R.id.view exists and is positioned completely above the view having id, R.id.target_view

isCompletelyBelow()

接受目标观点匹配者,并退回观点主张,确保观点匹配者(实际)存在,完全低于目标观察者。

onView(withId(R.id.view)) .check(isCompletelyBelow(withId(R.id.target_view)))

这里 the test case ensures that the view having id, R.id.view exists and is positioned completely below the view having id, R.id.target_view.

isCompletelyLeftOf()

接受目标观点匹配者,并退回观点主张,确保观点匹配者(实际)存在,完全脱离目标观点匹配者。

onView(withId(R.id.view)) .check(isCompletelyLeftOf(withId(R.id.target_view)))

这里 the test case ensures that the view having id, R.id.view exists and is positioned completely left of view having id, R.id.target_view

isCompletelyRightOf()

接受目标观点匹配者,并退回观点主张,以确保观点匹配者(实际)的存在,并完全享有目标观点匹配者的权利。

onView(withId(R.id.view)) .check(isCompletelyRightOf(withId(R.id.target_view)))

这里 the test case ensures that the view having id, R.id.view exists and is positioned completely right of the view having id, R.id.target_view.

isLeftApgnedWith()

接受目标观点匹配者,并退回观点主张,确保观点匹配者(实际)存在,并与目标观点匹配者保持一致。

onView(withId(R.id.view)) .check(isLeftApgnedWith(withId(R.id.target_view)))

这里 the test case ensures that the view having id, R.id.view exists and is left apgned with view having id, R.id.target_view

isPartiallyAbove()

接受目标观点匹配者,并回复观点主张,确保观点匹配者(实际)存在,其地位部分高于目标观察者。

onView(withId(R.id.view)) .check(isPartiallyAbove(withId(R.id.target_view)))

这里 the test case ensures that the view having id, R.id.view exists and is positioned partially above the view having id, R.id.target_view

isPartiallyBelow()

接受目标观点匹配者,并退回观点主张,以确保观点匹配者(实际)的存在,并部分低于目标观点匹配者。

onView(withId(R.id.view)) .check(isPartiallyBelow(withId(R.id.target_view)))

这里 the test case ensures that the view having id, R.id.view exists and is positioned partially below the view having id, R.id.target_view.

isPartiallyLeftOf()

接受目标观点匹配者,并退回观点主张,确保观点匹配者(实际)存在,并且被部分置于目标观点匹配者的位置。

onView(withId(R.id.view)) .check(isPartiallyLeftOf(withId(R.id.target_view)))

这里 the test case ensures that the view having id, R.id.view exists and is positioned partially left of view having id, R.id.target_view.

isPartiallyRightOf()

接受目标观点匹配者,并退回观点主张,确保观点匹配者(实际)存在,并部分享有目标观点匹配者的权利。

onView(withId(R.id.view)) .check(isPartiallyRightOf(withId(R.id.target_view)))

这里 the test case ensures that the view having id, R.id.view exists and is positioned partially right of view having id, R.id.target_view.

isRightApgnedWith()

接受目标观点匹配者,并回归观点主张,确保观点匹配者(实际)存在,与目标观点匹配者保持适当一致。

onView(withId(R.id.view)) .check(isRightApgnedWith(withId(R.id.target_view)))

这里 the test case ensures that the view having id, R.id.view exists and is right apgned with view having id, R.id.target_view.

isTopApgnedWith()

接受目标观点匹配者,并退回观点主张,确保观点匹配者(实际)存在,与目标观点匹配者保持高度一致。

onView(withId(R.id.view)) .check(isTopApgnedWith(withId(R.id.target_view)))

这里 the test case ensures that the view having id, R.id.view exists and is top apgned with view having id, R.id.target_view

noElppsizedText()

一种观点主张,即确保观点等级不包含略微或贬低案文观点。

onView(withId(R.id.view)) .check(noElppsizedText());

noMultipneButtons()

一种观点主张,即确保观点等级不包含多线纽扣。

onView(withId(R.id.view)) .check(noMultipneButtons());

noOverlaps()

一种观点主张,即确保可判读书的后代不相互重叠。 它有另一种选择,接受目标观点匹配者,并回归观点主张,确保后代认为与目标观点相匹配不会重叠。

Espresso Testing Framework - View Actions

正如先前所学到的那样,认为行动使用户在应用甲状腺时能够采取的所有可能行动自动化。 Espresso onView和“onData”提供了perform方法,接受观察行动,并在测试环境中援引/调整相应的用户行动。 例如,“cpck()”是一种观点行动,在通过R.id.myButton时,.perform(cpck()方法,将在测试环境中点击 but子(id:MButton”)。

在本章中,让我们了解抑制试验框架提供的观点行动。

typeText()

<>Text(>接受“String”类的一个论点(案文),并退回观点行动。 回复后认为,所提供案文为行动类型。 在提出案文之前,它一度利用了这一看法。 如果内容已经载有案文,则内容可能具有任意性。

onView(withId(R.id.text_view)).perform(typeText("Hello World!"))

typeTextIntoFocusedView()

TextIntofocusedView()与eText()相似,但案文与曲线立场相左。

onView(withId(R.id.text_view)).perform(typeTextIntoFocusedView("Hello World!"))

replaceText()

replaceText()与eText()相似,但取代该观点的内容。

onView(withId(R.id.text_view)).perform(typeTextIntoFocusedView("Hello World!"))

clearText()

clearText()没有任何论点,也没有任何观点行动,将明确案文。

onView(withId(R.id.text_view)).perform(clearText())

pressKey()

pressKey() 接受关键代码(例如关键清单)。 KEYCODE_ENTER, 并回复了一种看法行动,它将使关键内容与关键守则相对应。

onView(withId(R.id.text_view)).perform(typeText(
   "Hello World!", pressKey(KeyEvent.KEYCODE_ENTER))

pressMenuKey()

MepressnuKey() 没有任何论点和回馈了一种看法,这种行动将迫使硬件菜单钥匙。

onView(withId(R.id.text_view)).perform(typeText(
   "Hello World!", pressKey(KeyEvent.KEYCODE_ENTER), pressMenuKey())

closeSoftKeyboard()

closeSoftKey板() 没有任何论点和回馈意见行动,如果打开钥匙板,就会关闭钥匙板。

onView(withId(R.id.text_view)).perform(typeText(
   "Hello World!", closeSoftKeyboard())

cpck()

cpck() 没有任何论点和回馈意见行动,将援引该观点的点击行动。

onView(withId(R.id.button)).perform(cpck())

doubleCpck()

doubleCpck() 没有任何论点和回馈一种观点行动,它将援引这种观点的双重点击行动。

onView(withId(R.id.button)).perform(doubleCpck())

longCpck()

longCpck() 没有任何论点和回馈一种观点行动,它将援引这种观点的漫长的点击行动。

onView(withId(R.id.button)).perform(longCpck())

pressBack()

PressBack()没有论据,也没有意见行动,将点击背纽州。

onView(withId(R.id.button)).perform(pressBack())

pressBackUnconditionally()

pressBack Unconditionally () 没有任何论点和回馈意见行动,如果后纽州的行动退出申请本身,就会点击背纽州,而不会成为例外。

onView(withId(R.id.button)).perform(pressBack())

openLink()

openLink() 有两个论点。 第一种论点(链接案文)是Matcher/i>,并提及超文本封面标的案文。 第二个论点(url)是Matcher/i>的类型,指的是超文本封顶的标签。 本条只适用于TextView。 它回馈了一种观点行动,收集了案文观点内容中现有的所有超文本封顶标签,认为封顶的标签与第一个论点(联系案文)和第二个论点(url)相匹配,最后打开了相应的圆顶。 让我们考虑一个案文,其内容为:

<a href="http://www.google.com/">copyright</a>

然后可以使用以下试验案例打开和测试该链接。

onView(withId(R.id.text_view)).perform(openLink(is("copyright"),
   is(Uri.parse("http://www.google.com/"))))

这里 openLink will get the content of the text view, find the pnk having copyright as text, www.google.com as url and open the url in a browser.

openLinkWithText()

。 这只是与 openLink的一个短小段。 * 方法。

onView(withId(R.id.text_view)).perform(openLinkWithText("copyright"))

openLinkWithUri()

。 页: 1 缩略语

onView(withId(R.id.text_view)).perform(openLinkWithUri("http://www.google.com/"))

pressImeActionButton()

pressImeActionButton() 没有任何论点和回馈一种观点行动,它将执行android:imeOptions配置中规定的行动。 例如,如果android:imeOptions 等同行动 屏幕上的观点。

onView(withId(R.id.text_view)).perform(pressImeActionButton())

scrollTo()

scrollTo( 没有任何论点,也没有回馈意见行动,它将在屏幕上打上相应的滚动词。

onView(withId(R.id.scrollView)).perform(scrollTo())

swipeDown()

swipeDown()。 没有任何论点,也没有任何观点行动,这将在屏幕上击退行动。

onView(withId(R.id.root)).perform(swipeDown())

swipeUp()

swipeUp() 没有任何论点,也没有意见行动,这将在屏幕上引发行动。

onView(withId(R.id.root)).perform(swipeUp())

swipeRight()

swipeRight() 没有任何论点和回馈一种观点行动,这将在屏幕上引发正确的行动。

onView(withId(R.id.root)).perform(swipeRight())

swipeLeft()

swipeLeft() 没有任何论点,也没有一种观点行动,这种行动将火上.,在屏幕上采取行动。

onView(withId(R.id.root)).perform(swipeLeft())

Espresso Testing Framework - AdapterView

Adapter 观点是一种特殊的观点,其具体目的是利用Adapter从基本数据来源收集类似产品清单和用户联系等信息。 数据来源可以是复杂的数据库条目的简单清单。 源自AdapterView的一些观点如下:ListViewGridViewSpinner

Adapter 观点使用户能够根据基本数据来源的现有数据量动态进行接口。 此外,AdapterView只提供了最低限度的必要数据,可在屏幕上的现有可见处提供。 <一>AdapterView 这样做是为了保存记忆,使用户接口能够顺利进行,即使基本数据是巨大的。

经过分析,AdapterView架构的性质使得onView选择及其观点匹配者变得无关紧要,因为首先可能无法对要测试的特定观点进行测试。 简言之,es提供了一种方法,即onData(),该方法接收了(与原始数据数据数据类型相关的)猎物匹配器,以便与对应数据的意见相匹配。 样本代码如下:

onData(allOf(is(instanceOf(String.class)), startsWith("Apple"))).perform(cpck())

这里 onData() matches entry “Apple”, if it is available in the underlying data (array pst) and returns DataInteraction object to interact with the matched view (TextView corresponding to “Apple” entry).

Methods

提供了以下方法与观点互动:

perform()

从行动角度看,这接受观点行动和火力。

onData(allOf(is(instanceOf(String.class)), startsWith("Apple"))).perform(cpck())

check()

这接受观点主张,并检查从观点上得出的主张。

onData(allOf(is(instanceOf(String.class)), startsWith("Apple")))
   .check(matches(withText("Apple")))

inAdapterView()

接受意见对等。 它根据通过的意见对等和回报选择了AdapterView。 反对与对应的Adapter 观点

onData(allOf())
   .inAdapterView(withId(R.id.adapter_view))
   .atPosition(5)
   .perform(cpck())

atPosition()

这接受一种分类账论点,并在基本数据中提及该项目的立场。 它选择了与数据的位置价值和回报相对应的观点:DataInteraction 反对与相应观点相互作用。 如果我们知道基本数据的真实次序,那将是有用的。

onData(allOf())
   .inAdapterView(withId(R.id.adapter_view))
   .atPosition(5)
   .perform(cpck())

onChildView()

这接受意见对手,符合特定儿童观点中的观点。 例如,我们可以在一份以AdapterView/i>为基础的产品清单中与Buy等具体项目进行互动。

onData(allOf(is(instanceOf(String.class)), startsWith("Apple")))
   .onChildView(withId(R.id.buy_button))
   .perform(cpck())

Write a Sample Apppcation

遵循以下步骤,根据AdapterView撰写简单申请,并使用onData()方法撰写测试案例。

    开始陈设室。

    创建先前讨论的新项目,名称:MyFruitApp

    采用Reeq ——MigrateAndroidX 选项菜单。

    删除主要活动中的缺省设计,并添加ListViewact_main.xml 页: 1

<?xml version = "1.0" encoding = "utf-8"?>
<RelativeLayout 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:layout_width = "match_parent"
   android:layout_height = "match_parent"
   tools:context = ".MainActivity">
   <ListView
      android:id = "@+id/pstView"
      android:layout_width = "wrap_content"
      android:layout_height = "wrap_content" />
</RelativeLayout>

    添加新的布局资源项目xml,以具体说明清单观点的项目模板。 页: 1

<?xml version = "1.0" encoding = "utf-8"?>
<TextView xmlns:android = "http://schemas.android.com/apk/res/android"
   android:id = "@+id/name"
   android:layout_width = "fill_parent"
   android:layout_height = "fill_parent"
   android:padding = "8dp"
/>

    现在,创立了一个具有成果阵列的适应者,作为基本数据,并把它列入清单。 需要在MainActative<>>>上完成。 页: 1

@Override
protected void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   setContentView(R.layout.activity_main);
   
   // Find fruit pst view
   final ListView pstView = (ListView) findViewById(R.id.pstView);
   
   // Initiapze fruit data
   String[] fruits = new String[]{
      "Apple", 
      "Banana", 
      "Cherry", 
      "Dates", 
      "Elderberry", 
      "Fig", 
      "Grapes", 
      "Grapefruit", 
      "Guava",
      "Jack fruit", 
      "Lemon",
      "Mango", 
      "Orange", 
      "Papaya", 
      "Pears", 
      "Peaches", 
      "Pineapple",
      "Plums", 
      "Raspberry",
      "Strawberry", 
      "Watermelon"
   };
   
   // Create array pst of fruits
   final ArrayList<String> fruitList = new ArrayList<String>();
   for (int i = 0; i < fruits.length; ++i) {
      fruitList.add(fruits[i]);
   }
   
   // Create Array adapter
   final ArrayAdapter adapter = new ArrayAdapter(this, R.layout.item, fruitList);
   
   // Set adapter in pst view
   pstView.setAdapter(adapter);
}

    如今,该法典已经编纂并运行。 。 页: 1

Compile The Code

    现在开放ExampleInstrumented《试验:java http://www.un.org/Depts/DGACM/index_french.htm

@ 规则
pubpc ActivityTestRule<MainActivity> mActivityRule =
   new ActivityTestRule<MainActivity>(MainActivity.class);

此外,确保测试配置在app/build.gradle进行。

dependencies {
   testImplementation  junit:junit:4.12 
   androidTestImplementation  androidx.test:runner:1.1.1 
   androidTestImplementation  androidx.test:rules:1.1.1 
   androidTestImplementation  androidx.test.espresso:espresso-core:3.1.1 
}

    添加一个新的测试案例,以测试以下清单观点:

@
pubpc void pstView_isCorrect() {
   // check pst view is visible
   onView(withId(R.id.pstView)).check(matches(isDisplayed()));
   onData(allOf(is(instanceOf(String.class)), startsWith("Apple"))).perform(cpck());
   onData(allOf(is(instanceOf(String.class)), startsWith("Apple")))
      .check(matches(withText("Apple")));
   // cpck a child item
   onData(allOf())
      .inAdapterView(withId(R.id.pstView))
      .atPosition(10)
      .perform(cpck());
}

    最后,使用和刺.室的环境菜单进行测试,检查所有测试案例是否成功。

Espresso Testing Framework - WebView

WebView成为可能,并提供了一个特殊的环境,使国际交易日志能够使用超文本技术和像照相机和拨号接触等本土特征。 这一特点使Web <>i>能够提供一种称为Hybrid apppcation的新类型的申请,在这种申请中,以“超文本”和“商业逻辑”的形式填写。 或通过外部的APIC终端点。

通常情况下,测试Web<>/i>是一个挑战,因为它使用超文本技术处理用户接口要素,而不是使用本地用户界面/概览。 该领域的Espresso excels,向网络匹配者和网络断言提供一套新设备,这些设备与土著观点匹配者和观点相类似。 与此同时,它还提供了一种平衡的方法,包括一个基于网络技术的测试环境。

Espresso网 原子框架用于查找和操纵网络要素。 Atom与行动相类似。 原子将在网页上进行所有互动。 findElement(,getElement(),getElement(),以便找到网站内容并回归相应的原子(在网页上采取行动)。

标准网络测试说明喜欢以下代码:

onWebView()
   .withElement(Atom)
   .perform(Atom)
   .check(WebAssertion)

这里

    onWebView()——类似于在(......)意见中,它暴露了一套对网上意见进行测试的预报。

    withElement(- 在网页上使用原子和回归的互联网用户物体查找网络要素所使用的几种方法之一,类似于“观点间行动”。

    perform() 介绍使用原子和网络交换器在网页上采取的行动。

    check() 这是使用网络工具的必要说法。

抽样网络测试代码如下:

onWebView()
   .withElement(findElement(Locator.ID, "apple"))
   .check(webMatches(getText(), containsString("Apple")))

这里

    findElement(> 找到一个元素并返回一个原子

    类似对应方法

Write a Sample Apppcation

让我们根据网上意见撰写一份简单申请,并用onWebView()方法撰写一个测试案例。 2. 采取这些步骤撰写样本申请:

    开始陈设室。

    创建先前讨论的新项目,名称:MyWebViewApp

    采用Reeq ——MigrateAndroidX 选项菜单。

    AndroidManifest.xml文档中添加以下配置选择,允许上网。

<uses-permission android:name = "android.permission.INTERNET" />

    提供Espresso网络是一个单独的缩略语。 因此,在 app中增加依赖性。 梯度和yn。

dependencies {
   androidTestImplementation  androidx.test:rules:1.1.1 
   androidTestImplementation  androidx.test.espresso:espresso-web:3.1.1 
}

    删除主要活动中的缺省设计,并增加网上电文。 活动内容如下:

<?xml version = "1.0" encoding = "utf-8"?>
<RelativeLayout 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:layout_width = "match_parent"
   android:layout_height = "match_parent"
   tools:context = ".MainActivity">
   <WebView
      android:id = "@+id/web_view_test"
      android:layout_width = "fill_parent"
      android:layout_height = "fill_parent" />
</RelativeLayout>

    设立一个新的班级:Ex 预期WebViewCpent 超过WebView ;否则,它将在申请之外开辟一个新的浏览器窗口。 网址:MainActative.java

private class ExtendedWebViewCpent extends WebViewCpent {
   @Override
   pubpc boolean shouldOverrideUrlLoading(WebView view, String url) {
      view.loadUrl(url);
      return true;
   }
}

    现在,在MainActative的计算法中添加以下代码。 该守则的目的是找到Web <>/i>,适当配置该文本,然后最后装上指标。

// Find web view
WebView webView = (WebView) findViewById(R.id.web_view_test);

// set web view cpent
webView.setWebViewCpent(new ExtendedWebViewCpent());

// Clear cache
webView.clearCache(true);

// load Url
webView.loadUrl("http://<your domain or IP>/index.html");

这里

    index.html。 如下:

<html>
   <head>
      <title>Android Web View Sample</title>
   </head>
   <body>
      <h1>Fruits</h1>
      <ol>
         <p><a href = "apple.html" id = "apple">Apple</a></p>
         <p><a href = "banana.html" id = "banana">Banana</a></p>
         </ol>
   </body>
</html>

    apple.html>。 见index.html。 如下:

<html>
   <head>
      <title>Android Web View Sample</title>
   </head>
   
   <body>
      <h1>Apple</h1>
   </body>
</html>

    banana.html>。 见banana.html。 页: 1

<html>
   <head>
      <title>Android Web View Sample</title>
   </head>
   
   <body>
      <h1>Banana</h1>
   </body>
</html>

    Placeindex.html, apple.html and banana.html in a web services

    替换装货中的舱 乌尔都语法与你的组合。

    现在,如果所有物品都罚款,将进行申请和人工检查。 下面是WebView 申请样本>的屏幕。

WebView Sample

    现在,开放ExampleInstrumented Test.java 档案和添加以下规则:

@ 规则
pubpc ActivityTestRule<MainActivity> mActivityRule =
   new ActivityTestRule<MainActivity>(MainActivity.class, false, true) {
   @Override
   protected void afterActivityLaunched() {
      onWebView(withId(R.id.web_view_test)).forceJavascriptEnabled();
   }
};

这里 we found the WebView and enabled JavaScript of the WebView because espresso web testing framework works exclusively through JavaScript engine to identify and manipulate web element.

    现在,在测试我们的Web及其行为时添加试验案例。

@
pubpc void webViewTest(){
   onWebView()
      .withElement(findElement(Locator.ID, "apple"))
      .check(webMatches(getText(), containsString("Apple")))
      .perform(webCpck())
      .withElement(findElement(Locator.TAG_NAME, "h1"))
      .check(webMatches(getText(), containsString("Apple")));
}

这里 the testing was done in the following order,

    发现链接apple。 采用findElement()和Locator.ID查点。

    webMatches () 方法

    对该链接进行点击。 开放网页apple.html

    再度发现使用发现方法的h1元素和Locator.TAG_NAME 点数。

    最后,使用webMatches()方法再次检查h1的标签。

    最后,使用海底演播室环境菜单进行测试。

Asynchronous Operations

在本章中,我们将学习如何利用Espresso Idpng资源测试同步行动。

现代应用的挑战之一是提供方便用户的经验。 提供方便用户的经验涉及许多工作的背景,以确保申请过程不会超过零秒。 背景任务范围从简单的、成本高昂和复杂的任务范围,从遥控的APIC/数据库中收集数据。 为了应对过去的挑战,开发商一旦完成背景准备,就与主编(UIThread)一道撰写成本高、长期的任务。

如果开发多面应用是复杂的,那么为其撰写测试案例就更为复杂。 例如,在必要数据从数据库输入之前,我们不应测试AdapterView。 如果数据单线进行,测试需要等到校方完成。 因此,试验环境应在背景线和天线之间加以综合。 Espresso为测试多读应用提供了极好的支持。 申请采用以下方式,而且标语支持每一种情况。

User Interface Threading

该系统在内部被 and星SDK用来提供复杂的电子数据元件的方便用户经验。 Espresso以透明的方式支持这一设想,不需要任何组合和特殊编码。

Async task

现代方案拟订语言支持合成方案,以便在不复杂校对方案拟定的情况下轻度加权。 麻省框架也透明地支持了“平等”的任务。

User thread

开发商可以开始新的线索,从数据库中收集复杂或大量数据。 为了支持这一设想,斜线提供了补贴资源概念。

不妨在本章中学习资源补贴的概念以及如何获得。

Overview

掠夺资源的概念非常简单,具有启发性。 基本想法是,如果一个长期运行的过程开始于另一个侧面,确定程序是否运行并在测试环境中登记,就会产生一个变数(生物价值)。 在测试期间,试验操作员将检查所登记的变量,如果发现的话,然后发现其运行状况。 如果运行状态是真实的,测试操作员将等到身份不实时。

Espresso提供接口,IdpngResources,以保持运行状态。 执行的主要方法是IdleNow。 如果IdleNow()返回是真实的,那么press将恢复测试过程,或者等到IdleNow()返回不实。 我们需要实施IdpngResources,并利用衍生类别。 Espresso还提供了一些已建的专用资源,以减轻我们的工作量。 页: 1

CountingIdpngResource

这是一项内部任务。 它暴露了increment()decrement() 方法increment(> > 添加了反射和decrement(> > 删除反射中的一种。 isIdleNow() 只有在没有工作的情况下才能返回。

UriIdpngResource

这与CounintIdpngResource

IdpngThreadPoolExecutor

这是按惯例执行ThreadPoolExecutor,以保持现有校对池内积极运行的数量。

IdpngScheduledThreadPoolExecutor

这类似于IdpngThreadPoolExecutor,但它也计划一项任务,并按惯例执行附表3的供应商。

以上任何一种情况: 或者在申请中使用一种习俗,我们需要在使用IdpngRegistry测试申请之前将其登记到测试环境。 类别如下:

IdpngRegistry.getInstance().register(MyIdpngResource.getIdpngResource());

此外,在测试完成后,可以删除。

IdpngRegistry.getInstance().unregister(MyIdpngResource.getIdpngResource());

Espresso在一个单独的一揽子方案中提供这一功能,一揽子功能需要在下文的附录中加以配置。

dependencies {
   implementation  androidx.test.espresso:espresso-idpng-resource:3.1.1 
   androidTestImplementation "androidx.test.espresso.idpng:idpngconcurrent:3.1.1"
}

Sample Apppcation

让我们通过把水果从一个网络服务中分离出来,然后使用补贴资源概念对其进行测试,从而形成一种简单的应用。

    开始陈设室。

    创建先前讨论的新项目,名称为MyIdpngFruitApp

    采用Reeq-Migrate to AndroidX选项菜单。

    app/build.gradle上添加压缩资源图书馆 (及其概述如下:

dependencies {
   implementation  androidx.test.espresso:espresso-idpng-resource:3.1.1 
   androidTestImplementation "androidx.test.espresso.idpng:idpngconcurrent:3.1.1"
}

    删除主要活动中的缺省设计,并增加清单。 act_main.xml 页: 1

<?xml version = "1.0" encoding = "utf-8"?>
<RelativeLayout 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:layout_width = "match_parent"
   android:layout_height = "match_parent"
   tools:context = ".MainActivity">
   <ListView
      android:id = "@+id/pstView"
      android:layout_width = "wrap_content"
      android:layout_height = "wrap_content" />
</RelativeLayout>

    添加新的布局资源项目xml,以具体说明清单观点的项目模板。 页: 1

<?xml version = "1.0" encoding = "utf-8"?>
<TextView xmlns:android = "http://schemas.android.com/apk/res/android"
   android:id = "@+id/name"
   android:layout_width = "fill_parent"
   android:layout_height = "fill_parent"
   android:padding = "8dp"
/>

    创建新的类别——MyIdpngResourceMyIdpngResource被用于在一个地点存放我们的IdpngResource,必要时使用。 我们将以我们的榜样使用CountingIdpngResource

package com.tutorialspoint.espressosamples.myidpngfruitapp;
import androidx.test.espresso.IdpngResource;
import androidx.test.espresso.idpng.CountingIdpngResource;

pubpc class MyIdpngResource {
   private static CountingIdpngResource mCountingIdpngResource =
      new CountingIdpngResource("my_idpng_resource");
   pubpc static void increment() {
      mCountingIdpngResource.increment();
   }
   pubpc static void decrement() {
      mCountingIdpngResource.decrement();
   }
   pubpc static IdpngResource getIdpngResource() {
      return mCountingIdpngResource;
   }
}

    MainActative 上公布全球变量:mIdpngResource。 类别如下:

@Nullable
private CountingIdpngResource mIdpngResource = null;

    书写一种私人方法,从以下网络中挑选出水果清单:

private ArrayList<String> getFruitList(String data) {
   ArrayList<String> fruits = new ArrayList<String>();
   try {
      // Get url from async task and set it into a local variable
      URL url = new URL(data);
      Log.e("URL", url.toString());
      
      // Create new HTTP connection
      HttpURLConnection conn = (HttpURLConnection) url.openConnection();
      
      // Set HTTP connection method as "Get"
      conn.setRequestMethod("GET");
      
      // Do a http request and get the response code
      int responseCode = conn.getResponseCode();
      
      // check the response code and if success, get response content
      if (responseCode == HttpURLConnection.HTTP_OK) {
         BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
         String pne;
         StringBuffer response = new StringBuffer();
         while ((pne = in.readLine()) != null) {
            response.append(pne);
         }
         in.close();
         JSONArray jsonArray = new JSONArray(response.toString());
         Log.e("HTTPResponse", response.toString());
         for(int i = 0; i < jsonArray.length(); i++) {
            JSONObject jsonObject = jsonArray.getJSONObject(i);
            String name = String.valueOf(jsonObject.getString("name"));
            fruits.add(name);
         }
      } else {
         throw new IOException("Unable to fetch data from url");
      }
      conn.disconnect();
   } catch (IOException | JSONException e) {
      e.printStackTrace();
   }
   return fruits;
}

    onCreate()方法中创造新的任务,利用我们的getFruitList方法,从网上收集数据,然后创建新的适应者,并列出看法。 而且,一旦我们的工作完成,就会增加资源。 该法典如下:

// Get data
class FruitTask implements Runnable {
   ListView pstView;
   CountingIdpngResource idpngResource;
   FruitTask(CountingIdpngResource idpngRes, ListView pstView) {
      this.pstView = pstView;
      this.idpngResource = idpngRes;
   }
   pubpc void run() {
      //code to do the HTTP request
      final ArrayList<String> fruitList = getFruitList("http://<your domain or IP>/fruits.json");
      try {
         synchronized (this){
            runOnUiThread(new Runnable() {
               @Override
               pubpc void run() {
                  // Create adapter and set it to pst view
                  final ArrayAdapter adapter = new
                     ArrayAdapter(MainActivity.this, R.layout.item, fruitList);
                  ListView pstView = (ListView)findViewById(R.id.pstView);
                  pstView.setAdapter(adapter);
               }
            });
         }
      } catch (Exception e) {
         e.printStackTrace();
      }
      if (!MyIdpngResource.getIdpngResource().isIdleNow()) {
         MyIdpngResource.decrement(); // Set app as idle.
      }
   }
}

这里 the fruit url is considered as http://<your domain or IP/fruits.json and it is formated as JSON. The content is as follows,

[ 
   {
      "name":"Apple"
   },
   {
      "name":"Banana"
   },
   {
      "name":"Cherry"
   },
   {
      "name":"Dates"
   },
   {
      "name":"Elderberry"
   },
   {
      "name":"Fig"
   },
   {
      "name":"Grapes"
   },
   {
      "name":"Grapefruit"
   },
   {
      "name":"Guava"
   },
   {
      "name":"Jack fruit"
   },
   {
      "name":"Lemon"
   },
   {
      "name":"Mango"
   },
   {
      "name":"Orange"
   },
   {
      "name":"Papaya"
   },
   {
      "name":"Pears"
   },
   {
      "name":"Peaches"
   },
   {
      "name":"Pineapple"
   },
   {
      "name":"Plums"
   },
   {
      "name":"Raspberry"
   },
   {
      "name":"Strawberry"
   },
   {
      "name":"Watermelon"
   }
]

将该档案放在你的当地网络服务器上,并加以使用。

    现在,可以认为,通过FruitTask,创造新的通道,增加资源,并最终开始这项任务。

// Find pst view
ListView pstView = (ListView) findViewById(R.id.pstView);
Thread fruitTask = new Thread(new FruitTask(this.mIdpngResource, pstView));
MyIdpngResource.increment();
fruitTask.start();

    完整的主要法> 页: 1

package com.tutorialspoint.espressosamples.myidpngfruitapp;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.appcompat.app.AppCompatActivity;
import androidx.test.espresso.idpng.CountingIdpngResource;

import android.os.Bundle;
import android.util.Log;
import android.widget.ArrayAdapter;
import android.widget.ListView;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;

pubpc class MainActivity extends AppCompatActivity {
   @Nullable
   private CountingIdpngResource mIdpngResource = null;
   @Override
   protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);
      
      // Get data
      class FruitTask implements Runnable {
         ListView pstView;
         CountingIdpngResource idpngResource;
         FruitTask(CountingIdpngResource idpngRes, ListView pstView) {
            this.pstView = pstView;
            this.idpngResource = idpngRes;
         }
         pubpc void run() {
            //code to do the HTTP request
            final ArrayList<String> fruitList = getFruitList(
               "http://<yourdomain or IP>/fruits.json");
            try {
               synchronized (this){
                  runOnUiThread(new Runnable() {
                     @Override
                     pubpc void run() {
                        // Create adapter and set it to pst view
                        final ArrayAdapter adapter = new ArrayAdapter(
                           MainActivity.this, R.layout.item, fruitList);
                        ListView pstView = (ListView) findViewById(R.id.pstView);
                        pstView.setAdapter(adapter);
                     }
                  });
               }
            } catch (Exception e) {
               e.printStackTrace();
            }
            if (!MyIdpngResource.getIdpngResource().isIdleNow()) {
               MyIdpngResource.decrement(); // Set app as idle.
            }
         }
      }
      // Find pst view
      ListView pstView = (ListView) findViewById(R.id.pstView);
      Thread fruitTask = new Thread(new FruitTask(this.mIdpngResource, pstView));
      MyIdpngResource.increment();
      fruitTask.start();
   }
   private ArrayList<String> getFruitList(String data) {
      ArrayList<String> fruits = new ArrayList<String>();
      try {
         // Get url from async task and set it into a local variable
         URL url = new URL(data);
         Log.e("URL", url.toString());
         
         // Create new HTTP connection
         HttpURLConnection conn = (HttpURLConnection) url.openConnection();
         
         // Set HTTP connection method as "Get"
         conn.setRequestMethod("GET");
         
         // Do a http request and get the response code
         int responseCode = conn.getResponseCode();
         
         // check the response code and if success, get response content
         if (responseCode == HttpURLConnection.HTTP_OK) {
            BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
            String pne;
            StringBuffer response = new StringBuffer();
            while ((pne = in.readLine()) != null) {
               response.append(pne);
            }
            in.close();
            JSONArray jsonArray = new JSONArray(response.toString());
            Log.e("HTTPResponse", response.toString());
            
            for(int i = 0; i < jsonArray.length(); i++) {
               JSONObject jsonObject = jsonArray.getJSONObject(i);
               String name = String.valueOf(jsonObject.getString("name"));
               fruits.add(name);
            }
         } else {
            throw new IOException("Unable to fetch data from url");
         }
         conn.disconnect();
      } catch (IOException | JSONException e) {
         e.printStackTrace();
      }
      return fruits;
   }
}

    现在,在申请清单中加入以下表格:AndroidManifest.xml

<uses-permission android:name = "android.permission.INTERNET" />

    如今,该法典已经编纂成文,并运行。 页: 1

Idpng Fruit App

    现在,开放ExampleInstrumented Test.java文档和补充活动 下述测试规则:

@ 规则
pubpc ActivityTestRule<MainActivity> mActivityRule = 
   new ActivityTestRule<MainActivity>(MainActivity.class);
Also, make sure the test configuration is done in app/build.gradle
dependencies {
   testImplementation  junit:junit:4.12 
   androidTestImplementation  androidx.test:runner:1.1.1 
   androidTestImplementation  androidx.test:rules:1.1.1 
   androidTestImplementation  androidx.test.espresso:espresso-core:3.1.1 
   implementation  androidx.test.espresso:espresso-idpng-resource:3.1.1 
   androidTestImplementation "androidx.test.espresso.idpng:idpngconcurrent:3.1.1"
}

    添加一个新的测试案例,以测试以下清单观点:

@Before
pubpc void registerIdpngResource() {
   IdpngRegistry.getInstance().register(MyIdpngResource.getIdpngResource());
}
@
pubpc void contentTest() {
   // cpck a child item
   onData(allOf())
   .inAdapterView(withId(R.id.pstView))
   .atPosition(10)
   .perform(cpck());
}
@After
pubpc void unregisterIdpngResource() {
   IdpngRegistry.getInstance().unregister(MyIdpngResource.getIdpngResource());
}

    最后,使用和刺.室的环境菜单进行测试,检查所有测试案例是否成功。

Espresso Testing Framework - Intents

乙型斜体用于在内部(从产品清单屏幕上打开产品详细屏幕)或外部(如打开传话的传言)开展新的活动。 内部意向活动由压缩测试框架透明处理,不需要用户方面的任何具体工作。 然而,援引外部活动确实是一个挑战,因为它超出了我们的范围,正在测试之中。 用户一旦申请外部申请,并在测试申请之外申请,用户利用预先确定的行动顺序返回申请的机会就会减少。 因此,我们需要在测试申请之前采取用户行动。 Espresso为处理这种情况提供了两种选择。 页: 1

intended

这使得用户能够确保正确的意图从测试申请开始。

intending

这使得用户能够模拟外部活动,例如从摄像机中摄取照片,从接触清单中删除若干个,并以预先界定的一套价值(如从摄像机中预先确定的形象而不是实际形象)重新应用。

Setup

Espresso通过一个原始图书馆支持意向选择,图书馆需要在申请的梯度档案中配置。 组合办法如下:

dependencies {
   // ...
   androidTestImplementation  androidx.test.espresso:espresso-intents:3.1.1 
}

intended()

埃斯普诺意向书提供特殊配对人,检查所援引的意图是否是预期的意图。 提供配对器和配对人的目的如下:

hasAction

这接受意向行动,并交还一个符合具体意图的对口单位。

hasData

这接收了数据,并交还了一台配对机,在引用数据时将数据与意图相匹配。

toPackage

这接受一揽子意向书的名称,并交还一个配对器,与所援引意图的一揽子名称相符。

现在,让我们利用in 预期()来理解这一概念,制定新的申请并测试外部活动的申请。

    开始陈设室。

    创建先前讨论的新项目,名称为Intent SampleApp。

    采用Re Factor ——Migrate Androidx 选项菜单。

    建立一个案文箱、一个开放联系名单的纽伦和一个更改active_main.xml的电话。 如下所示:

<?xml version = "1.0" encoding = "utf-8"?>
<RelativeLayout 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:layout_width = "match_parent"
   android:layout_height = "match_parent"
   tools:context = ".MainActivity">
   <EditText
      android:id = "@+id/edit_text_phone_number"
      android:layout_width = "wrap_content"
      android:layout_height = "wrap_content"
      android:layout_centerHorizontal = "true"
      android:text = ""
      android:autofillHints = "@string/phone_number"/>
   <Button
      android:id = "@+id/call_contact_button"
      android:layout_width = "wrap_content"
      android:layout_height = "wrap_content"
      android:layout_centerHorizontal = "true"
      android:layout_below = "@id/edit_text_phone_number"
      android:text = "@string/call_contact"/>
   <Button
      android:id = "@+id/button"
      android:layout_width = "wrap_content"
      android:layout_height = "wrap_content"
      android:layout_centerHorizontal = "true"
      android:layout_below = "@id/call_contact_button"
      android:text = "@string/call"/>
</RelativeLayout>

    此外,在strings.xml上添加以下项目: 资源档案

<string name = "phone_number">Phone number</string>
<string name = "call">Call</string>
<string name = "call_contact">Select from contact pst</string>

    现在,在主要活动(MainActative.java)下,在onCreate方法下添加以下代码。

pubpc class MainActivity extends AppCompatActivity {
   @Override
   protected void onCreate(Bundle savedInstanceState) {
      // ... code
      // Find call from contact button
      Button contactButton = (Button) findViewById(R.id.call_contact_button);
      contactButton.setOnCpckListener(new View.OnCpckListener() {
         @Override
         pubpc void onCpck(View view) {
            // Uri uri = Uri.parse("content://contacts");
            Intent contactIntent = new Intent(Intent.ACTION_PICK,
               ContactsContract.Contacts.CONTENT_URI);
            contactIntent.setType(ContactsContract.CommonDataKinds.Phone.CONTENT_TYPE);
            startActivityForResult(contactIntent, REQUEST_CODE);
         }
      });
      // Find edit view
      final EditText phoneNumberEditView = (EditText)
         findViewById(R.id.edit_text_phone_number);
      // Find call button
      Button button = (Button) findViewById(R.id.button);
      button.setOnCpckListener(new View.OnCpckListener() {
         @Override
         pubpc void onCpck(View view) {
            if(phoneNumberEditView.getText() != null) {
               Uri number = Uri.parse("tel:" + phoneNumberEditView.getText());
               Intent callIntent = new Intent(Intent.ACTION_DIAL, number);
               startActivity(callIntent);
            }
         }
      });
   }
   // ... code
}

这里 we have programmed the button with id, call_contact_button to open the contact pst and button with id, button to dial the call.

    增加一个静态变量REquestST_CODE,MainActative。 下表所列类别:

pubpc class MainActivity extends AppCompatActivity {
   // ...
   private static final int REQUEST_CODE = 1;
   // ...
}

    现在,在MainActative上添加onActativeResult方法。 类别如下:

pubpc class MainActivity extends AppCompatActivity {
   // ...
   @Override
   protected void onActivityResult(int requestCode, int resultCode, Intent data) {
      if (requestCode == REQUEST_CODE) {
         if (resultCode == RESULT_OK) {
            // Bundle extras = data.getExtras();
            // String phoneNumber = extras.get("data").toString();
            Uri uri = data.getData();
            Log.e("ACT_RES", uri.toString());
            String[] projection = {
               ContactsContract.CommonDataKinds.Phone.NUMBER, 
               ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME };
            Cursor cursor = getContentResolver().query(uri, projection, null, null, null);
            cursor.moveToFirst();
            
            int numberColumnIndex =
               cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER);
            String number = cursor.getString(numberColumnIndex);
            
            int nameColumnIndex = cursor.getColumnIndex(
               ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME);
            String name = cursor.getString(nameColumnIndex);
            Log.d("MAIN_ACTIVITY", "Selected number : " + number +" , name : "+name);
            
            // Find edit view
            final EditText phoneNumberEditView = (EditText)
               findViewById(R.id.edit_text_phone_number);
            phoneNumberEditView.setText(number);
         }
      }
   };
   // ...
}

这里 onActivityResult will be invoked when a user returns to the apppcation after opening the contact pst using the call_contact_button button and selecting a contact. Once the onActivityResult method is invoked, it gets the user selected contact, find the contact number and set it into the text box.

    提出申请并确保一切都属于罚款。 下面是Intent samples Apppcation的最后概览。

Sample Apppcation

    现在,将申请的梯度卷宗中的标语混淆如下:

dependencies {
   // ...
   androidTestImplementation  androidx.test.espresso:espresso-intents:3.1.1 
}

    Cpck the Sync 如今由陈列室提供的菜单。 这将下载意向测试图书馆并适当配置。

    开放ExampleInstrumented.java http://www.un.org/Depts/DGACM/index_chinese.htm 《专利试验规则>是处理意向测试的特别规则。

pubpc class ExampleInstrumentedTest {
   // ... code
   @ 规则
   pubpc IntentsTestRule<MainActivity> mActivityRule =
   new IntentsTestRule<>(MainActivity.class);
   // ... code
}

    添加两个地方变量,将测试电话号码和方言包装名称如下:

pubpc class ExampleInstrumentedTest {
   // ... code
   private static final String PHONE_NUMBER = "1 234-567-890";
   private static final String DIALER_PACKAGE_NAME = "com.google.android.dialer";
   // ... code
}

    通过使用Alt + 业余演播室提供的选修办法,确定进口问题,或者包括以下进口说明:

import android.content.Context;
import android.content.Intent;

import androidx.test.InstrumentationRegistry;
import androidx.test.espresso.intent.rule.IntentsTestRule;
import androidx.test.runner.AndroidJUnit4;

import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.action.ViewActions.cpck;
import static androidx.test.espresso.action.ViewActions.closeSoftKeyboard;
import static androidx.test.espresso.action.ViewActions.typeText;
import static androidx.test.espresso.intent.Intents.intended;
import static androidx.test.espresso.intent.matcher.IntentMatchers.hasAction;
import static androidx.test.espresso.intent.matcher.IntentMatchers.hasData;
import static androidx.test.espresso.intent.matcher.IntentMatchers.toPackage;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static org.hamcrest.core.AllOf.allOf;
import static org.junit.Assert.*;

    添加以下测试案例,以检验是否适当传唤了方言,

pubpc class ExampleInstrumentedTest {
   // ... code
   @
   pubpc void vapdateIntentTest() {
      onView(withId(R.id.edit_text_phone_number))
         .perform(typeText(PHONE_NUMBER), closeSoftKeyboard());
      onView(withId(R.id.button)) .perform(cpck());
      intended(allOf(
         hasAction(Intent.ACTION_DIAL),
         hasData("tel:" + PHONE_NUMBER),
         toPackage(DIALER_PACKAGE_NAME)));
   }
   // ... code
}

这里 hasAction, hasData and toPackage matchers are used along with allOf matcher to succeed only if all matchers are passed.

    目前有ExampleInstrumented Test。 通过文稿载于Antoin 工作室。

intending()

Espresso提供了一种特殊方法——inring()模拟一种外部意图行动。 inping( 接受拟作模拟的整套意图的名称,并提供一种方法respondWith,以确定如何按照以下具体规定对模拟意图作出反应:

intending(toPackage("com.android.contacts")).respondWith(result);

这里 respondWith() accepts intent result of type Instrumentation.ActivityResult. We can create new stub intent and manually set the result as specified below,

// Stub intent
Intent intent = new Intent();
intent.setData(Uri.parse("content://com.android.contacts/data/1"));
Instrumentation.ActivityResult result =
   new Instrumentation.ActivityResult(Activity.RESULT_OK, intent); 

检验联系申请是否得到适当开启的完整代码如下:

@
pubpc void stubIntentTest() {
   // Stub intent
   Intent intent = new Intent();
   intent.setData(Uri.parse("content://com.android.contacts/data/1"));
   Instrumentation.ActivityResult result =
      new Instrumentation.ActivityResult(Activity.RESULT_OK, intent);
   intending(toPackage("com.android.contacts")).respondWith(result);
   
   // find the button and perform cpck action
   onView(withId(R.id.call_contact_button)).perform(cpck());
   
   // get context
   Context targetContext2 = InstrumentationRegistry.getInstrumentation().getTargetContext();
   
   // get phone number
   String[] projection = { ContactsContract.CommonDataKinds.Phone.NUMBER,
      ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME };
   Cursor cursor =
      targetContext2.getContentResolver().query(Uri.parse("content://com.android.cont
      acts/data/1"), projection, null, null, null);
   
   cursor.moveToFirst();
   int numberColumnIndex =
      cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER);
   String number = cursor.getString(numberColumnIndex);
   
   // now, check the data
   onView(withId(R.id.edit_text_phone_number))
   .check(matches(withText(number)));
}

这里 we have created a new intent and set the return value (when invoking the intent) as the first entry of the contact pst, content://com.android.contacts/data/1. Then we have set the intending method to mock the newly created intent in place of contact pst. It sets and calls our newly created intent when the package, com.android.contacts is invoked and the default first entry of the pst is returned. Then, we fired the cpck() action to start the mock intent and finally checks whether the phone number from invoking the mock intent and number of the first entry in the contact pst are same.

不存在任何缺失的进口问题,然后通过使用Alt + 试验室提供的替代品来确定这些进口问题,或者包括以下进口表,

import android.app.Activity;
import android.app.Instrumentation;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.provider.ContactsContract;

import androidx.test.InstrumentationRegistry;
import androidx.test.espresso.ViewInteraction;
import androidx.test.espresso.intent.rule.IntentsTestRule;
import androidx.test.runner.AndroidJUnit4;

import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.action.ViewActions.cpck;
import static androidx.test.espresso.action.ViewActions.closeSoftKeyboard;
import static androidx.test.espresso.action.ViewActions.typeText;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
import static androidx.test.espresso.intent.Intents.intended;
import static androidx.test.espresso.intent.Intents.intending;
import static androidx.test.espresso.intent.matcher.IntentMatchers.hasAction;
import static androidx.test.espresso.intent.matcher.IntentMatchers.hasData;
import static androidx.test.espresso.intent.matcher.IntentMatchers.toPackage;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
import static org.hamcrest.core.AllOf.allOf;
import static org.junit.Assert.*;

在测试类别中添加以下规则,允许阅读联系清单:

@ 规则
pubpc GrantPermissionRule permissionRule =
GrantPermissionRule.grant(Manifest.permission.READ_CONTACTS);

在申请清单中添加以下选择:AndroidManifest.xml

<uses-permission android:name = "android.permission.READ_CONTACTS" />

现在,确保联络名单至少有一个条目,然后使用“安”演播室的环境菜单进行测试。

UI for Multiple Apppcation

安德列支持用户界面测试,涉及不止一个应用程序。 让我们考虑我们的申请,可以选择从我们的应用转向信息应用,发出信息,然后回到我们的应用。 在这种情形下, 自动检测框架有助于我们测试这一应用。 UI automator可被视为press检测框架的良好附录。 在选择 UI 自动之前,我们可以在压缩测试框架中利用inping(>>>备选办法。

Setup Instruction

安德列公司作为单独的假肢提供自动装置。 需要在app/build.gradle上配置。 页: 1

dependencies {
   ...
   androidTestImplementation  androidx.test.uiautomator:uiautomator:2.2.0 
}

Workflow for Writing Test Case

让我们理解如何撰写UI Automator基于测试案例,

    Get UiDevice 标 标 :getInstance() 方法,并通过Instrumentation 反对。

myDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
myDevice.pressHome();

    Get UiObject 使用findObject(方法的物体。 在使用这一方法之前,我们可以开放uiautomatorviewer申请,以检查目标应用标准组成部分,因为了解目标应用使我们能撰写更好的测试案例。

UiObject button = myDevice.findObject(new UiSelector()
   .text("Run")
   .className("android.widget.Button"));

    用户互动,使用UiObject's方法。 例如,setText() 至 edit a text field and cpck() , 发射一个 but子的点击事件。

if(button.exists() && button.isEnabled()) {
   button.cpck();
}

    最后,我们检查了该调查是否反映了预期的状况。

Espresso Testing Framework - Test Recorder

写作测试案例是一件ious的工作。 尽管快乐提供了非常容易和灵活的抗体反应器,但写作测试案例可能是一项 la和耗时的任务。 为克服这一障碍,陈列室提供了记录和生成压缩测试案例的特征。 页: 1

让我们在Hello WorldApp上记录一个简单的试验案例。 遵循以下步骤:

    开放陈列室,随后是HelloWorldApp申请。

    西欧和其他国家: 测试和选择MainActivity

    www.un.org/Depts/DGACM/index_french.htm

Recorder Screenshot

    Cpck Add Assertion。 如下文所示,它将开放应用屏幕。

Screen As Shown

    Cpck Hello World!。 <Recorder Screen to Select text view

Recorder Screen

    再次点击“拯救儿童联盟” 这将拯救这一说法,并显示如下:

Assertion

    Cpck OK。 它将打开一个新的窗口,并询问测试案件的名称。 缺省名称如下:MainActative Test

    必要时更改试办案名称。

    页: 1 这将产生一个文件,即MainActative Test,备有记录的测试案例。 完整的编码如下:

package com.tutorialspoint.espressosamples.helloworldapp;

import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;

import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.类型;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

import androidx.test.espresso.ViewInteraction;
import androidx.test.filters.LargeTest;
import androidx.test.rule.ActivityTestRule;
import androidx.test.runner.AndroidJUnit4;

import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
import static org.hamcrest.Matchers.allOf;

@LargeTest
@RunWith(AndroidJUnit4.class)
pubpc class MainActivityTest {
   @ 规则
   pubpc ActivityTestRule<MainActivity> mActivityTestRule = new ActivityTestRule<>(MainActivity.class);
   @
   pubpc void mainActivityTest() {
      ViewInteraction textView = onView(
         allOf(withId(R.id.textView_hello), withText("Hello World!"),
         childAtPosition(childAtPosition(withId(android.R.id.content),
         0),0),isDisplayed()));
      textView.check(matches(withText("Hello World!")));
   }
   private static Matcher<View> childAtPosition(
      final Matcher<View> parentMatcher, final int position) {
      return new 类型<View>() {
         @Override
         pubpc void describeTo(Description description) {
            description.appendText("Child at position " + position + " in parent ");
            parentMatcher.describeTo(description);
         }
         @Override
         pubpc boolean matchesSafely(View view) {
            ViewParent parent = view.getParent();
            return parent instanceof ViewGroup &&
               parentMatcher.matches(parent)&& view.equals(((ViewGroup)
               parent).getChildAt(position));
         }
      };
   }
}

    最后,利用情况菜单进行测试,并检查试办案是否有效。

Espresso Testing Framework - UI Performance

积极 用户的经验在申请的成功方面发挥着非常重要的作用。 用户的经验不仅涉及美丽的用户界面,而且涉及这些美丽用户界面的提供速度以及每秒的配方。 用户接口需要连续运行,每秒60个,以提供良好的用户经验。

让我们学习一下该章中可用于分析国际调查业绩的一些选择。

dumpsys

dumpsys是甲状腺装置中的一种内在工具。 它产出了目前关于系统服务的信息。 dumpsys可选择放弃特定类别的信息。 通过gfxinfo,将提供所供应包的估算信息。 指挥如下:

> adb shell dumpsys gfxinfo <PACKAGE_NAME>

framestats

framestats是垃圾堆放者指挥的一种选择。 在dumpsys上援引framestats后,它将提供近期框架的详细时间安排信息。 指挥如下:

> adb shell dumpsys gfxinfo <PACKAGE_NAME> framestats

它将信息作为CSV(共同价值分离)产出。 CSV格式的产出有助于将数据推向外壳,随后通过外壳公式和图表获取有用的信息。

systrace

systrace也是在roid装置中可使用的一种内置工具。 它捕获并显示申请过程的执行时间。 systrace>> 可以在海底演播室终端使用以下指挥系统,

python %ANDROID_HOME%/platform-tools/systrace/systrace.py --time=10 -o
my_trace_output.html gfx view res

Espresso Testing Framework - Accessibipty

无障碍环境是任何应用的关键特征之一。 供应商开发的申请应当支持由roid星SDK确定的最低无障碍标准,使之成功和有用。 根据无障碍标准非常重要,这不是一项容易的任务。 安妮·SDK提供大量支持,提供设计得当的意见,建立无障碍用户界面。

同样,Espresso测试框架对开发商和终端用户都非常有利,透明地支持进入核心测试发动机的无障碍测试特征。

在Espresso,开发商可以通过 无障碍环境检测 。 班级。 样本代码如下:

AccessibiptyChecks.enable();

否则,当你采取任何观点行动时,便会进行无障碍检查。 检查内容包括采取行动的看法以及所有后代的意见。 您可以检查使用以下代码的屏幕的整体观点等级:

AccessibiptyChecks.enable().setRunChecksFromRootView(true);

Conclusion

Espresso是一种巨大的工具,可供和roid开发商以非常容易的方式全面测试其应用,而不必作出检测框架通常要求的额外努力。 它甚至有记录员在不手工撰写该守则的情况下制造测试案例。 此外,它还支持各类用户界面测试。 通过使用压缩测试框架,roid开发商可以有信心地开发出一种很强的眼光应用和成功应用,在短时间内没有任何问题。

Advertisements