Khung thử nghiệm Espresso - Hướng dẫn nhanh

Nói chung, kiểm thử tự động hóa di động là một nhiệm vụ khó khăn và đầy thử thách. Tính khả dụng của Android cho các thiết bị và nền tảng khác nhau khiến việc thử nghiệm tự động hóa trên thiết bị di động trở nên tẻ nhạt. Để làm cho nó dễ dàng hơn, Google đã chấp nhận thử thách và phát triển khung Espresso. Nó cung cấp một API rất đơn giản, nhất quán và linh hoạt để tự động hóa và kiểm tra các giao diện người dùng trong một ứng dụng Android. Các bài kiểm tra Espresso có thể được viết bằng cả Java và Kotlin, một ngôn ngữ lập trình hiện đại để phát triển ứng dụng Android.

API Espresso rất đơn giản và dễ học. Bạn có thể dễ dàng thực hiện kiểm tra giao diện người dùng Android mà không cần kiểm tra đa luồng phức tạp. Google Drive, Maps và một số ứng dụng khác hiện đang sử dụng Espresso.

Đặc điểm của Espresso

Một số tính năng nổi bật được Espresso hỗ trợ như sau,

  • API rất đơn giản và vì vậy, dễ học.

  • Khả năng mở rộng cao và linh hoạt.

  • Cung cấp mô-đun riêng biệt để kiểm tra thành phần WebView của Android.

  • Cung cấp mô-đun riêng biệt để xác thực cũng như giả lập Ý định Android.

  • Cung cấp đồng bộ hóa tự động giữa ứng dụng của bạn và các thử nghiệm.

Ưu điểm của Espresso

Bây giờ hãy để chúng tôi những lợi ích của Espresso là gì.

  • Tương thích ngược

  • Dễ dàng cài đặt.

  • Chu kỳ kiểm tra ổn định cao.

  • Hỗ trợ các hoạt động thử nghiệm bên ngoài ứng dụng.

  • Hỗ trợ JUnit4

  • Tự động hóa giao diện người dùng thích hợp để viết các bài kiểm tra hộp đen.

Trong chương này, chúng ta hãy hiểu cách cài đặt espresso framework, cấu hình nó để viết các bài kiểm tra espresso và thực thi nó trong ứng dụng Android của chúng ta.

Điều kiện tiên quyết

Espresso là một khung kiểm tra giao diện người dùng để kiểm tra ứng dụng Android được phát triển bằng ngôn ngữ Java / Kotlin sử dụng Android SDK. Vì vậy, yêu cầu duy nhất của espresso là phát triển ứng dụng sử dụng Android SDK trong Java hoặc Kotlin và bạn nên có Android Studio mới nhất.

Danh sách các mục cần được định cấu hình đúng cách trước khi chúng tôi bắt đầu làm việc trong khuôn khổ espresso như sau:

  • Cài đặt Java JDK mới nhất và định cấu hình biến môi trường JAVA_HOME.

  • Cài đặt Android Studio mới nhất (phiên bản 3.2. Hoặc cao hơn).

  • Cài đặt SDK Android mới nhất bằng Trình quản lý SDK và định cấu hình biến môi trường ANDROID_HOME.

  • Cài đặt Gradle Build Tool mới nhất và định cấu hình biến môi trường GRADLE_HOME.

Định cấu hình khung công tác kiểm tra Espress

Ban đầu, khung thử nghiệm cà phê espresso được cung cấp như một phần của thư viện Hỗ trợ Android. Sau đó, nhóm Android cung cấp một thư viện Android mới, AndroidX và chuyển phát triển khung thử nghiệm cà phê espresso mới nhất vào thư viện. Sự phát triển mới nhất (Android 9.0, API cấp 28 trở lên) của khung thử nghiệm cà phê espresso sẽ được thực hiện trong thư viện AndroidX.

Việc bao gồm khung thử nghiệm espresso trong một dự án cũng đơn giản như việc thiết lập khung thử nghiệm espresso như một phần phụ thuộc trong tệp phân loại ứng dụng, app / build.gradle. Cấu hình hoàn chỉnh như sau,

Sử dụng thư viện hỗ trợ Android,

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'
}

Sử dụng thư viện AndroidX,

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'
}

testIricalmentationRunner trong android / defaultConfig đặt lớp AndroidJUnitRunner để chạy các bài kiểm tra thiết bị. Dòng đầu tiên trong phần phụ thuộc bao gồm khung thử nghiệm JUnit , dòng thứ hai trong phần phụ thuộc bao gồm thư viện người chạy thử nghiệm để chạy các trường hợp thử nghiệm và cuối cùng dòng thứ ba trong phần phụ thuộc bao gồm khung thử nghiệm cà phê espresso.

Theo mặc định, Android studio đặt khung thử nghiệm espresso (thư viện hỗ trợ Android) làm phụ thuộc trong khi tạo dự án android và gradle sẽ tải xuống thư viện cần thiết từ kho lưu trữ Maven. Hãy để chúng tôi tạo một ứng dụng android Hello world đơn giản và kiểm tra xem khung thử nghiệm espresso có được định cấu hình đúng cách hay không.

Các bước để tạo một ứng dụng Android mới được mô tả bên dưới:

  • Khởi động Android Studio.

  • Chọn Tệp → Mới → Dự án mới.

  • Nhập Tên ứng dụng (HelloWorldApp) và miền Công ty (espressosa samples.tutorialspoint.com) rồi nhấp vào Tiếp theo .

Để tạo Dự án Android,

  • Chọn API tối thiểu là API 15: Android 4.0.3 (IceCreamSandwich) rồi nhấp vào Tiếp theo.

Để nhắm mục tiêu Thiết bị Android,

  • Chọn Hoạt động trống và sau đó nhấp vào Tiếp theo .

Để thêm một hoạt động vào Điện thoại di động,

  • Nhập tên cho hoạt động chính và sau đó nhấp vào Kết thúc .

Để định cấu hình Hoạt động,

  • Khi một dự án mới được tạo, hãy mở tệp app / build.gradle và kiểm tra nội dung của nó. Nội dung của tệp được chỉ định bên dưới,

apply plugin: 'com.android.application'
android {
   compileSdkVersion 28
   defaultConfig {
      applicationId "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: 'libs', 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'
}

Dòng cuối cùng chỉ định sự phụ thuộc của khung thử nghiệm espresso. Theo mặc định, thư viện hỗ trợ Android được định cấu hình. Chúng ta có thể cấu hình lại ứng dụng để sử dụng thư viện AndroidX bằng cách nhấp vào RefactorDi chuyển sang AndroidX trong menu.

Để chuyển sang Androidx,

  • Bây giờ, app / build.gradle thay đổi như được chỉ định bên dưới,

apply plugin: 'com.android.application'
android {
   compileSdkVersion 28
   defaultConfig {
      applicationId "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: 'libs', 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'
}

Bây giờ, dòng cuối cùng bao gồm khung thử nghiệm cà phê espresso từ thư viện AndroidX.

Cài đặt thiết bị

Trong quá trình thử nghiệm, nên tắt hoạt ảnh trên thiết bị Android, được sử dụng để thử nghiệm. Điều này sẽ giảm bớt sự nhầm lẫn trong khi kiểm tra tài nguyên định danh.

Hãy để chúng tôi xem cách tắt hoạt ảnh trên thiết bị Android - (Cài đặt → Tùy chọn nhà phát triển),

  • Tỷ lệ hoạt ảnh cửa sổ

  • Tỷ lệ hoạt ảnh chuyển tiếp

  • Thang thời lượng của người tạo hoạt ảnh

Nếu menu Tùy chọn nhà phát triển không có sẵn trong màn hình Cài đặt , thì hãy nhấp vào Số bản dựng có sẵn bên trong tùy chọn Giới thiệu về điện thoại nhiều lần. Điều này sẽ bật menu Tùy chọn dành cho nhà phát triển .

Trong chương này, chúng ta hãy xem cách chạy thử nghiệm bằng Android studio.

Mỗi ứng dụng Android đều có hai loại kiểm tra -

  • Kiểm tra chức năng / đơn vị

  • Kiểm tra thiết bị

Kiểm tra chức năng không cần ứng dụng Android thực tế được cài đặt và khởi chạy trong thiết bị hoặc trình giả lập và kiểm tra chức năng. Nó có thể được khởi chạy trong chính bảng điều khiển mà không cần gọi ứng dụng thực tế. Tuy nhiên, các bài kiểm tra thiết bị đo cần ứng dụng thực tế được khởi chạy để kiểm tra các chức năng như giao diện người dùng và tương tác với người dùng. Theo mặc định, các bài kiểm tra Đơn vị được viết bằngsrc/test/java/ thư mục và các bài kiểm tra Dụng cụ được viết bằng src/androidTest/java/thư mục. Android studio cung cấp menu ngữ cảnh Chạy cho các lớp thử nghiệm để chạy thử nghiệm được viết trong các lớp thử nghiệm đã chọn. Theo mặc định, ứng dụng Android có hai lớp - ExampleUnitTest trong src / kiểm tra thư mục và ExampleInstrumentedTest trong src / androidTest thư mục.

Để chạy kiểm tra đơn vị mặc định, hãy chọn ExampleUnitTest trong Android studio, nhấp chuột phải vào nó và sau đó nhấp vào Chạy 'ExampleUnitTest' như được hiển thị bên dưới,

Chạy thử nghiệm đơn vị

Thao tác này sẽ chạy thử nghiệm đơn vị và hiển thị kết quả trong bảng điều khiển như trong ảnh chụp màn hình sau:

Kiểm tra đơn vị thành công

Để chạy thử nghiệm thiết bị đo đạc mặc định, hãy chọn Ví dụ về Thử nghiệm thiết bị đo đạc trong android studio, nhấp chuột phải vào nó và sau đó nhấp vào Chạy 'Thử nghiệm thiết bị đo đạc mẫu' như được hiển thị bên dưới,

Chạy thử nghiệm thiết bị đo

Thao tác này sẽ chạy thử nghiệm đơn vị bằng cách khởi chạy ứng dụng trong thiết bị hoặc trình giả lập và hiển thị kết quả trong bảng điều khiển như trong ảnh chụp màn hình sau:

Kiểm tra thiết bị đã chạy thành công.

Trong chương này, chúng ta hãy hiểu những điều cơ bản về JUnit , khuôn khổ kiểm thử đơn vị phổ biến được phát triển bởi cộng đồng Java mà trên đó, khung thử nghiệm espresso được xây dựng.

JUnit là tiêu chuẩn thực tế để kiểm tra đơn vị ứng dụng Java. Mặc dù, nó phổ biến cho thử nghiệm đơn vị, nó cũng hỗ trợ và cung cấp đầy đủ cho thử nghiệm thiết bị đo đạc. Thư viện thử nghiệm Espresso mở rộng các lớp JUnit cần thiết để hỗ trợ thử nghiệm thiết bị dựa trên Android.

Viết một bài kiểm tra đơn vị đơn giản

Hãy để chúng tôi tạo một lớp Java, Tính toán (Computation.java) và viết phép toán đơn giản, Phép tính tổngphép nhân . Sau đó, chúng tôi sẽ viết các trường hợp thử nghiệm bằng JUnit và kiểm tra nó bằng cách chạy các trường hợp thử nghiệm.

  • Khởi động Android Studio.

  • Mở HelloWorldApp đã tạo trong chương trước.

  • Tạo một tệp, Computation.java trong ứng dụng / src / main / java / com / tutorialspoint / espressosa samples / helloworldapp / và viết hai hàm - SumMultiply như được chỉ định bên dưới,

package com.tutorialspoint.espressosamples.helloworldapp;
public class Computation {
   public Computation() {}
   public int Sum(int a, int b) {
      return a + b;
   }
   public int Multiply(int a, int b) {
      return a * b;
   }
}
  • Tạo một tệp, ComputationUnitTest.java trong ứng dụng / src / test / java / com / tutorialspoint / espressosa samples / helloworldapp và viết các trường hợp kiểm thử đơn vị để kiểm tra chức năng Sum và Multiply như được chỉ định bên dưới

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

Ở đây, chúng tôi đã sử dụng hai thuật ngữ mới - @Testkhẳng địnhEquals . Nói chung, JUnit sử dụng chú thích Java để xác định các trường hợp thử nghiệm trong một lớp và thông tin về cách thực thi các trường hợp thử nghiệm. @Test là một trong những chú thích Java như vậy, chỉ định rằng hàm cụ thể là một trường hợp thử nghiệm junit. khẳng địnhEquals là một hàm để khẳng định rằng đối số đầu tiên (giá trị mong đợi) và đối số thứ hai (giá trị được tính toán) là bằng nhau và giống nhau. JUnit cung cấp một số phương pháp xác nhận cho các tình huống thử nghiệm khác nhau.

  • Bây giờ, chạy ComputationUnitTest trong Android studio bằng cách nhấp chuột phải vào lớp và gọi tùy chọn Run 'ComputationUnitTest' như đã giải thích trong chương trước. Điều này sẽ chạy các trường hợp thử nghiệm đơn vị và báo cáo thành công.

Kết quả kiểm tra đơn vị tính toán như hình dưới đây:

Chú thích

Khuôn khổ JUnit sử dụng chú thích rộng rãi . Một số chú thích quan trọng như sau:

  • @Test

  • @Before

  • @After

  • @BeforeClass

  • @AfterClass

  • @Rule

Chú thích @Test

@Test là chú thích rất quan trọng trong khuôn khổ JUnit . @Test được sử dụng để phân biệt một phương thức bình thường với phương pháp trường hợp thử nghiệm. Sau khi một phương thức được trang trí bằng chú thích @Test , thì phương thức cụ thể đó được coi là một Test case và sẽ được chạy bởi JUnit Runner . JUnit Runner là một lớp đặc biệt, được sử dụng để tìm và chạy các trường hợp thử nghiệm JUnit có sẵn bên trong các lớp java. Hiện tại, chúng tôi đang sử dụng tùy chọn bản dựng của Android Studio để chạy các bài kiểm tra đơn vị (lần lượt chạy JUnit Runner ). Một mã mẫu như sau,

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

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

@Trước

@Before annotation được sử dụng để chỉ một phương thức, phương thức này cần được gọi trước khi chạy bất kỳ phương thức thử nghiệm nào có sẵn trong một lớp thử nghiệm cụ thể. Ví dụ trong mẫu của chúng tôi, các tính toán đối tượng có thể được tạo ra trong một phương pháp riêng biệt và chú thích với @Before để nó sẽ chạy trước cả sum_isCorrectmultiply_isCorrect trường hợp thử nghiệm. Mã hoàn chỉnh như sau,

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

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

@Sau

@After tương tự như @Before , nhưng phương thức được chú thích bằng @After sẽ được gọi hoặc thực thi sau khi chạy mỗi trường hợp thử nghiệm. Mã mẫu như sau,

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

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

@BeforeClass

@BeforeClass tương tự như @Before , nhưng phương thức được chú thích với @BeforeClass sẽ chỉ được gọi hoặc thực thi một lần trước khi chạy tất cả các trường hợp thử nghiệm trong một lớp cụ thể. Nó hữu ích để tạo đối tượng sử dụng nhiều tài nguyên như đối tượng kết nối cơ sở dữ liệu. Điều này sẽ giảm thời gian thực thi một tập hợp các trường hợp kiểm thử. Phương thức này cần phải tĩnh để hoạt động bình thường. Trong mẫu của chúng tôi, chúng tôi có thể tạo đối tượng tính toán một lần trước khi chạy tất cả các trường hợp thử nghiệm như được chỉ định bên dưới,

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

public class ComputationUnitTest {
   private static Computation computation = null;
   @BeforeClass
   public static void CreateComputationObject() {
      computation = new Computation();
   }
   @Test
   public void sum_isCorrect() {
      assertEquals(4, computation.Sum(2,2));
   }
   @Test
   public void multiply_isCorrect() {
      assertEquals(4, computation.Multiply(2,2));
   }
}

@Sau giờ học

@AfterClass tương tự như @BeforeClass , nhưng phương thức được chú thích với @AfterClass sẽ chỉ được gọi hoặc thực thi một lần sau khi tất cả các trường hợp thử nghiệm trong một lớp cụ thể được chạy. Phương thức này cũng cần phải tĩnh để hoạt động tốt. Mã mẫu như sau:

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

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

@Qui định

Chú thích @Rule là một trong những điểm nổi bật của JUnit . Nó được sử dụng để thêm hành vi vào các trường hợp thử nghiệm. Chúng tôi chỉ có thể chú thích các trường thuộc loại TestRule . Nó thực sự cung cấp tính năng thiết lập được cung cấp bởi @Before@After chú thích nhưng trong một cách hiệu quả và tái sử dụng. Ví dụ: chúng tôi có thể cần một thư mục tạm thời để lưu trữ một số dữ liệu trong trường hợp thử nghiệm. Thông thường, chúng ta cần tạo một thư mục tạm thời trước khi chạy trường hợp thử nghiệm (sử dụng chú thích @Before hoặc @BeforeClass) và hủy nó sau khi trường hợp thử nghiệm được chạy (sử dụng chú thích @After hoặc @AfterClass). Thay vào đó, chúng ta có thể sử dụng lớp TemporaryFolder (thuộc loại TestRule ) do khung công tác JUnit cung cấp để tạo một thư mục tạm thời cho tất cả các trường hợp thử nghiệm của chúng tôi và thư mục tạm thời sẽ bị xóa khi và khi trường hợp thử nghiệm được chạy. Chúng ta cần tạo một biến mới kiểu TemporaryFolder và cần chú thích bằng @Rule như được chỉ định bên dưới,

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;

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

Lệnh thực hiện

Trong JUnit , các phương thức được chú thích bằng các chú thích khác nhau sẽ được thực thi theo thứ tự cụ thể như hình dưới đây,

  • @BeforeClass

  • @Rule

  • @Before

  • @Test

  • @After

  • @AfterClass

Quả quyết

Khẳng định là một cách để kiểm tra xem giá trị mong đợi của trường hợp thử nghiệm có khớp với giá trị thực của kết quả trường hợp thử nghiệm hay không. JUnit cung cấp xác nhận cho các kịch bản khác nhau; một số khẳng định quan trọng được liệt kê bên dưới -

  • fail() - Để thực hiện một cách rõ ràng một trường hợp kiểm thử không thành công.

  • assertTrue(boolean test_condition) - Kiểm tra xem test_condition có đúng không

  • assertFalse(boolean test_condition) - Kiểm tra xem test_condition có sai không

  • assertEquals(expected, actual) - Kiểm tra xem cả hai giá trị đều bằng nhau

  • assertNull(object) - Kiểm tra xem đối tượng có rỗng không

  • assertNotNull(object) - Kiểm tra xem đối tượng không rỗng

  • assertSame(expected, actual) - Séc mà cả hai đều đề cập đến cùng một đối tượng.

  • assertNotSame(expected, actual) - Séc mà cả hai đều đề cập đến các đối tượng khác nhau.

Trong chương này, chúng ta hãy tìm hiểu các thuật ngữ của khung thử nghiệm espresso, cách viết một trường hợp thử nghiệm espresso đơn giản và quy trình hoặc kiến ​​trúc hoàn chỉnh của khung thử nghiệm espresso.

Tổng quat

Espresso cung cấp một số lượng lớn các lớp để kiểm tra giao diện người dùng và sự tương tác của người dùng với một ứng dụng Android. Chúng có thể được nhóm thành năm loại như được chỉ định bên dưới:

Á hậu JUnit

Khung thử nghiệm Android cung cấp một Á hậu, AndroidJUnitRunner để chạy các trường hợp thử nghiệm espresso được viết trong các trường hợp thử nghiệm kiểu JUnit3 và JUnit4. Nó dành riêng cho ứng dụng android và nó xử lý một cách minh bạch việc tải các trường hợp thử nghiệm espresso và ứng dụng đang được thử nghiệm cả trong thiết bị thực tế hoặc trình giả lập, thực hiện các trường hợp thử nghiệm và báo cáo kết quả của các trường hợp thử nghiệm. Để sử dụng AndroidJUnitRunner trong trường hợp thử nghiệm, chúng tôi cần chú thích lớp thử nghiệm bằng cách sử dụng @RunWith annotation và sau đó chuyển đối số AndroidJUnitRunner như được chỉ định bên dưới:

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

Quy tắc JUnit

Khung thử nghiệm Android cung cấp một quy tắc, ActivityTestRule để khởi chạy một hoạt động android trước khi thực hiện các trường hợp thử nghiệm. Nó khởi chạy hoạt động trước mỗi phương thức được chú thích bằng @ Test` và @Before. Nó sẽ chấm dứt hoạt động sau khi phương thức được chú thích bằng @After. Một mã mẫu như sau,

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

Ở đây, MainActivity là hoạt động được khởi chạy trước khi chạy một trường hợp thử nghiệm và bị hủy sau khi trường hợp thử nghiệm cụ thể được chạy.

ViewMatchers

Espresso cung cấp số lượng lớn các lớp đối sánh chế độ xem (trong gói androidx.test.espresso.matcher.ViewMatchers ) để khớp và tìm các phần tử / chế độ xem giao diện người dùng trong hệ thống phân cấp chế độ xem của màn hình hoạt động android. Phương thức onView của Espresso nhận một đối số duy nhất của kiểu Matcher (View matchers), tìm giao diện người dùng tương ứng và trả về đối tượng ViewInteraction tương ứng . Đối tượng ViewInteraction được trả về bởi phương thức onView có thể được sử dụng thêm để gọi các hành động như nhấp vào chế độ xem phù hợp hoặc có thể được sử dụng để xác nhận chế độ xem đã khớp. Một mã mẫu để tìm chế độ xem có văn bản, “Hello World!” là như sau,

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

Ở đây, withText là một trình đối sánh, có thể được sử dụng để đối sánh chế độ xem giao diện người dùng có văn bản “Hello World!”

ViewActions

Espresso cung cấp số lượng lớn các lớp hành động chế độ xem (trong androidx.test.espresso.action.ViewActions) để gọi hành động khác nhau trên chế độ xem đã chọn / phù hợp. Khi onView khớp và trả về đối tượng ViewInteraction , bất kỳ hành động nào cũng có thể được gọi bằng cách gọi phương thức "thực hiện" của đối tượng ViewInteraction và chuyển nó với các hành động xem thích hợp. Mã mẫu để nhấp vào chế độ xem phù hợp như sau,

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

Tại đây, hành động nhấp của chế độ xem phù hợp sẽ được gọi.

ViewAssertions

Tương tự như đối sánh chế độ xem và hành động xem, Espresso cung cấp một số lượng lớn xác nhận chế độ xem (trong gói androidx.test.espresso.assertion.ViewAssertions ) để khẳng định chế độ xem phù hợp là những gì chúng tôi mong đợi. Khi onView khớp và trả về đối tượng ViewInteraction , bất kỳ xác nhận nào cũng có thể được kiểm tra bằng cách sử dụng phương pháp kiểm tra của ViewInteraction bằng cách chuyển nó với xác nhận chế độ xem thích hợp. Mã mẫu để khẳng định rằng chế độ xem phù hợp như sau,

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

Ở đây, các đối sánh chấp nhận trình đối sánh chế độ xem và xác nhận chế độ xem trả về, có thể được kiểm tra bằng phương pháp kiểm tra của ViewInteraction .

Quy trình làm việc của Khung thử nghiệm Espresso

Hãy để chúng tôi hiểu cách hoạt động của khung thử nghiệm cà phê espresso và cách nó cung cấp các tùy chọn để thực hiện bất kỳ loại tương tác nào của người dùng một cách đơn giản và linh hoạt. Quy trình làm việc của một ca kiểm tra cà phê espresso như được mô tả bên dưới,

  • Như chúng ta đã biết trước đó, Android JUnit Á hậu, AndroidJUnit4 sẽ chạy các trường hợp thử nghiệm Android. Các trường hợp thử nghiệm espresso cần được đánh dấu bằng @RunWith (AndroidJUnut.class) . Đầu tiên, AndroidJUnit4 sẽ chuẩn bị môi trường để chạy các trường hợp thử nghiệm. Nó khởi động thiết bị hoặc trình giả lập Android được kết nối, cài đặt ứng dụng và đảm bảo rằng ứng dụng được kiểm tra ở trạng thái sẵn sàng. Nó sẽ chạy các trường hợp thử nghiệm và báo cáo kết quả.

  • Espresso cần ít nhất một quy tắc JUnit của loại ActivityTestRule để chỉ định hoạt động. Người chạy Android JUnit sẽ bắt đầu hoạt động được khởi chạy bằng ActivityTestRule .

  • Mọi trường hợp thử nghiệm cần có tối thiểu lệnh gọi phương thức onView hoặc onDate (được sử dụng để tìm các chế độ xem dựa trên dữ liệu như AdapterView ) để khớp và tìm chế độ xem mong muốn. onView hoặc onData trả về đối tượng ViewInteraction .

  • Khi đối tượng ViewInteraction được trả về, chúng ta có thể gọi một hành động của dạng xem đã chọn hoặc kiểm tra dạng xem cho dạng xem mong đợi của chúng ta bằng cách sử dụng xác nhận.

  • Hành động có thể được gọi bằng cách sử dụng phương thức thực hiện của đối tượng ViewInteraction bằng cách chuyển bất kỳ một trong các hành động xem có sẵn.

  • Xác nhận có thể được gọi bằng cách sử dụng phương thức kiểm tra của đối tượng ViewInteraction bằng cách chuyển bất kỳ một trong các xác nhận chế độ xem có sẵn.

Biểu đồ sơ đồ của Dòng công việc như sau,

Ví dụ về khẳng định xem

Hãy để chúng tôi viết một trường hợp thử nghiệm đơn giản để tìm chế độ xem văn bản có “Hello World!” văn bản trong ứng dụng “HelloWorldApp” của chúng tôi và sau đó xác nhận nó bằng cách sử dụng xác nhận chế độ xem. Mã hoàn chỉnh như sau,

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)
public class ExampleInstrumentedTest {
   @Rule
   public ActivityTestRule<MainActivity> mActivityTestRule = new ActivityTestRule<>(MainActivity.class);
   @Test
   public void view_isCorrect() {
      onView(withText("Hello World!")).check(matches(isDisplayed()));
   }
   @Test
   public void useAppContext() {
      // Context of the app under test.
      Context appContext = InstrumentationRegistry.getTargetContext();
      assertEquals("com.tutorialspoint.espressosamples.helloworldapp", appContext.getPackageName());
   }
}

Ở đây, chúng tôi đã sử dụng trình so khớp chế độ xem withText để tìm chế độ xem văn bản có “Hello World!” văn bản và khớp với xác nhận chế độ xem để khẳng định rằng chế độ xem văn bản được hiển thị đúng. Sau khi trường hợp thử nghiệm được gọi trong Android Studio, nó sẽ chạy trường hợp thử nghiệm và báo cáo thông báo thành công như bên dưới.

trường hợp thử nghiệm view_isCorrect

Khung Espresso cung cấp nhiều trình so khớp chế độ xem. Mục đích của trình đối sánh là so khớp một chế độ xem bằng cách sử dụng các thuộc tính khác nhau của chế độ xem như Id, Văn bản và tính khả dụng của chế độ xem con. Mỗi đối sánh khớp với một thuộc tính cụ thể của chế độ xem và áp dụng cho loại chế độ xem cụ thể. Ví dụ: trình đối sánh withId khớp với thuộc tính Id của chế độ xem và áp dụng cho tất cả chế độ xem, trong khi trình đối sánh withText khớp với thuộc tính Văn bản của chế độ xem và chỉ áp dụng cho TextView .

Trong chương này, chúng ta hãy tìm hiểu các bộ phối trộn khác nhau được cung cấp bởi khung thử nghiệm cà phê espresso cũng như tìm hiểu thư viện Hamcrest mà các bộ trộn cà phê espresso được xây dựng trên đó.

Thư viện Hamcrest

Thư viện Hamcrest là một thư viện quan trọng trong phạm vi khung thử nghiệm cà phê espresso. Bản thân Hamcrest là một khuôn khổ để viết các đối tượng so khớp. Espresso framework sử dụng rộng rãi thư viện Hamcrest và mở rộng nó bất cứ khi nào cần thiết để cung cấp các bộ so khớp đơn giản và có thể mở rộng.

Hamcrest cung cấp một chức năng đơn giản assertThat và một bộ sưu tập của quẹt để khẳng định bất kỳ đối tượng. khẳng định Điều đó có ba đối số và chúng như được hiển thị bên dưới -

  • Chuỗi (mô tả bài kiểm tra, tùy chọn)

  • Đối tượng (thực tế)

  • Matcher (dự kiến)

Hãy để chúng tôi viết một ví dụ đơn giản để kiểm tra xem một đối tượng danh sách có giá trị mong đợi hay không.

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

Ở đây, hasItem trả về một trình so khớp, kiểm tra xem danh sách thực tế có giá trị được chỉ định là một trong các mục hay không.

Hamcrest có rất nhiều trình kết hợp cài sẵn và cũng có các tùy chọn để tạo trình kết hợp mới. Một số công cụ so khớp tích hợp quan trọng hữu ích trong khung thử nghiệm cà phê espresso như sau:

bất cứ điều gì - luôn luôn phù hợp

Đối sánh dựa trên lôgic

  • allOf - chấp nhận bất kỳ số lượng đối sánh nào và chỉ đối sánh nếu tất cả các đối sánh đều thành công.

  • anyOf - chấp nhận bất kỳ số lượng đối sánh nào và đối sánh nếu bất kỳ một đối sánh nào thành công.

  • not - chấp nhận một trình kết hợp và chỉ khớp nếu trình kết hợp không thành công và ngược lại.

Đối sánh dựa trên văn bản

  • equalToIgnoringCase - được sử dụng để kiểm tra xem đầu vào thực tế có bằng với chuỗi dự kiến ​​bỏ qua không.

  • equalToIgnoringWhiteSpace - được sử dụng để kiểm tra xem đầu vào thực tế có bằng với chuỗi đã chỉ định bỏ qua chữ hoa và khoảng trắng hay không.

  • containsString - được sử dụng để kiểm tra xem đầu vào thực tế có chứa chuỗi được chỉ định hay không.

  • endsWith - được sử dụng để kiểm tra xem đầu vào thực tế có bắt đầu bằng chuỗi được chỉ định hay không.

  • startsWith - được sử dụng để kiểm tra xem thực tế đầu vào có kết thúc bằng chuỗi được chỉ định hay không.

Đối sánh dựa trên số

  • closeTo - được sử dụng để kiểm tra xem liệu đầu vào thực tế có gần với số lượng mong đợi hay không.

  • greaterThan - được sử dụng để kiểm tra xem liệu đầu vào thực tế có lớn hơn số lượng mong đợi hay không.

  • greaterThanOrEqualTo - được sử dụng để kiểm tra xem liệu đầu vào thực tế có lớn hơn hoặc bằng số lượng mong đợi hay không.

  • lessThan - được sử dụng để kiểm tra xem liệu đầu vào thực tế có nhỏ hơn số lượng mong đợi hay không.

  • lessThanOrEqualTo - được sử dụng để kiểm tra xem liệu đầu vào thực tế có nhỏ hơn hoặc bằng số lượng mong đợi hay không.

Đối tượng dựa trên đối tượng

  • equalTo - được sử dụng để kiểm tra xem đầu vào thực tế có bằng với đối tượng mong đợi hay không

  • hasToString - được sử dụng để kiểm tra xem đầu vào thực tế có phương thức toString hay không.

  • instanceOf - được sử dụng để kiểm tra xem đầu vào thực tế có phải là trường hợp của lớp mong đợi hay không.

  • isCompatibleType - được sử dụng để kiểm tra xem đầu vào thực tế có tương thích với kiểu mong đợi hay không.

  • notNullValue - được sử dụng để kiểm tra xem đầu vào thực tế có phải là giá trị rỗng hay không.

  • sameInstance - được sử dụng để kiểm tra xem đầu vào thực tế và dự kiến ​​có giống nhau không.

  • hasProperty - được sử dụng để kiểm tra xem đầu vào thực tế có thuộc tính mong đợi hay không

là - Đường hoặc cắt ngắn cho bằng nhau

Người đối sánh

Espresso cung cấp phương thức onView () để khớp và tìm các chế độ xem. Nó chấp nhận các đối sánh chế độ xem và trả về đối tượng ViewInteraction để tương tác với chế độ xem đã khớp. Danh sách các trình so khớp chế độ xem được sử dụng thường xuyên được mô tả bên dưới:

withId ()

withId () chấp nhận một đối số kiểu int và đối số này tham chiếu đến id của khung nhìn. Nó trả về một đối sánh, đối sánh với chế độ xem bằng cách sử dụng id của chế độ xem. Mã mẫu như sau,

onView(withId(R.id.testView))

withText ()

withText () chấp nhận một đối số kiểu chuỗi và đối số này tham chiếu đến giá trị của thuộc tính văn bản của chế độ xem. Nó trả về một đối sánh, đối sánh với chế độ xem bằng cách sử dụng giá trị văn bản của chế độ xem. Nó chỉ áp dụng cho TextView . Mã mẫu như sau,

onView(withText("Hello World!"))

withContentDescription ()

withContentDescription () chấp nhận một đối số kiểu chuỗi và đối số đề cập đến giá trị của thuộc tính mô tả nội dung của chế độ xem. Nó trả về một đối sánh, đối sánh với chế độ xem bằng cách sử dụng mô tả của chế độ xem. Mã mẫu như sau,

onView(withContentDescription("blah"))

Chúng ta cũng có thể chuyển id tài nguyên của giá trị văn bản thay vì chính văn bản.

onView(withContentDescription(R.id.res_id_blah))

hasContentDescription ()

hasContentDescription () không có đối số. Nó trả về một đối sánh, đối sánh với chế độ xem có bất kỳ mô tả nội dung nào. Mã mẫu như sau,

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

withTagKey ()

withTagKey () chấp nhận một đối số kiểu chuỗi và đối số tham chiếu đến khóa thẻ của chế độ xem. Nó trả về một đối sánh, đối sánh với chế độ xem bằng cách sử dụng khóa thẻ của nó. Mã mẫu như sau,

onView(withTagKey("blah"))

Chúng tôi cũng có thể chuyển id tài nguyên của tên thẻ thay vì chính tên thẻ.

onView(withTagKey(R.id.res_id_blah))

withTagValue ()

withTagValue () chấp nhận đối số kiểu Matcher <Đối tượng> và đối số tham chiếu đến giá trị thẻ của chế độ xem. Nó trả về một đối sánh, đối sánh với chế độ xem sử dụng giá trị thẻ của nó. Mã mẫu như sau,

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

Dưới đây, là hamcrest khớp.

withClassName ()

withClassName () chấp nhận một đối số kiểu Matcher <Chuỗi> và đối số tham chiếu đến giá trị tên lớp của chế độ xem. Nó trả về một trình so khớp, khớp với chế độ xem sử dụng tên lớp của nó. Mã mẫu như sau,

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

Ở đây, kết thúcWith là Hamcrest matcher và trả về Matcher <Chuỗi>

withHint ()

withHint () chấp nhận một đối số kiểu Matcher <Chuỗi> và đối số tham chiếu đến giá trị gợi ý của chế độ xem. Nó trả về một so khớp, khớp với chế độ xem bằng cách sử dụng gợi ý của chế độ xem. Mã mẫu như sau,

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

withInputType ()

withInputType () chấp nhận một đối số kiểu int và đối số tham chiếu đến kiểu đầu vào của dạng xem. Nó trả về một trình so khớp, khớp với chế độ xem sử dụng kiểu đầu vào của nó. Mã mẫu như sau,

onView(withInputType(TYPE_CLASS_DATETIME))

Ở đây, TYPE_CLASS_DATETIME đề cập đến ngày và giờ hỗ trợ chế độ xem chỉnh sửa.

withResourceName ()

withResourceName () chấp nhận một đối số kiểu Matcher <Chuỗi> và đối số tham chiếu đến giá trị tên lớp của chế độ xem. Nó trả về một trình so khớp, khớp với chế độ xem sử dụng tên tài nguyên của chế độ xem. Mã mẫu như sau,

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

Nó cũng chấp nhận đối số chuỗi. Mã mẫu như sau,

onView(withResourceName("my_res_name"))

withAlpha ()

withAlpha () chấp nhận một đối số kiểu float và đối số này tham chiếu đến giá trị alpha của dạng xem. Nó trả về một đối sánh, đối sánh với chế độ xem bằng cách sử dụng giá trị alpha của chế độ xem. Mã mẫu như sau,

onView(withAlpha(0.8))

withEffectiveVisibility ()

withEffectiveVisibility () chấp nhận một đối số kiểu ViewMatchers.Visibility và đối số đề cập đến khả năng hiển thị hiệu quả của chế độ xem. Nó trả về một đối sánh, đối sánh với chế độ xem bằng cách sử dụng khả năng hiển thị của chế độ xem. Mã mẫu như sau,

onView(withEffectiveVisibility(withEffectiveVisibility.INVISIBLE))

withSpinnerText ()

withSpinnerText () chấp nhận một đối số kiểu Matcher <Chuỗi> và đối số này tham chiếu đến giá trị của chế độ xem được chọn hiện tại của Spinner. Nó trả về một trình so khớp, khớp với con quay dựa trên giá trị toString của mục đã chọn. Mã mẫu như sau,

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

Nó chấp nhận đối số chuỗi hoặc id tài nguyên của chuỗi. Mã mẫu như sau,

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

withSubstring ()

withSubString () tương tự như withText () ngoại trừ nó giúp kiểm tra chuỗi con của giá trị văn bản của khung nhìn.

onView(withSubString("Hello"))

hasLinks ()

hasLinks () không có đối số và nó trả về một đối sánh, khớp với dạng xem có liên kết. Nó chỉ áp dụng cho TextView. Mã mẫu như sau,

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

Ở đây, allOf là một trình khớp Hamcrest. allOf trả về một trình so khớp, khớp với tất cả các giá trị được chuyển vào trong so khớp và ở đây, nó được sử dụng để khớp một chế độ xem cũng như kiểm tra xem chế độ xem có liên kết trong giá trị văn bản của nó hay không.

hasTextColor ()

hasTextColor () chấp nhận một đối số duy nhất kiểu int và đối số này tham chiếu đến id tài nguyên của màu. Nó trả về một đối sánh, đối sánh TextView dựa trên màu của nó. Nó chỉ áp dụng cho TextView . Mã mẫu như sau,

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

hasEllipsizedText ()

hasEllipsizedText () không có đối số. Nó trả về một trình so khớp, khớp với TextView có văn bản dài và được đánh dấu chấm lửng (đầu tiên .. mười .. cuối cùng) hoặc bị cắt bỏ (đầu tiên…). Mã mẫu như sau,

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

hasMultilineText ()

hasMultilineText () không có đối số. Nó trả về một trình so khớp, khớp với TextView có bất kỳ văn bản nhiều dòng nào. Mã mẫu như sau,

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

hasBackground ()

hasBackground () chấp nhận một đối số duy nhất kiểu int và đối số này tham chiếu đến id tài nguyên của tài nguyên nền. Nó trả về một trình so khớp, khớp với chế độ xem dựa trên tài nguyên nền của nó. Mã mẫu như sau,

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

hasErrorText ()

hasErrorText () chấp nhận một đối số kiểu Matcher <Chuỗi> và đối số này tham chiếu đến giá trị chuỗi lỗi (EditText) của chế độ xem. Nó trả về một trình so khớp, khớp với chế độ xem bằng cách sử dụng chuỗi lỗi của chế độ xem. Điều này chỉ áp dụng cho EditText . Mã mẫu như sau,

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

Nó cũng chấp nhận đối số chuỗi. Mã mẫu như sau,

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

hasImeAction ()

hasImeAction () chấp nhận một đối số kiểu Matcher <Integer> và đối số này tham chiếu đến các phương thức đầu vào được hỗ trợ (EditText) của chế độ xem. Nó trả về một đối sánh, đối sánh với chế độ xem bằng cách sử dụng phương thức nhập được hỗ trợ của chế độ xem. Điều này chỉ áp dụng cho EditText . Mã mẫu như sau,

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

Ở đây, EditorInfo.IME_ACTION_GO nằm trên các tùy chọn phương thức nhập. hasImeAction () cũng chấp nhận đối số số nguyên. Mã mẫu như sau,

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

supportsInputMethods ()

supportsInputMethods () không có đối số. Nó trả về một so khớp, phù hợp với dạng xem nếu nó hỗ trợ các phương thức nhập. Mã mẫu như sau,

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

isRoot ()

isRoot () không có đối số. Nó trả về một trình so khớp, phù hợp với chế độ xem gốc. Mã mẫu như sau,

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

được hiển thị()

isDisplayed () không có đối số. Nó trả về một trình so khớp, khớp với chế độ xem hiện đang được hiển thị. Mã mẫu như sau,

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

isDisplayingAtLeast ()

isDisplayingAtLeast () chấp nhận một đối số duy nhất kiểu int. Nó trả về một trình so khớp, phù hợp với chế độ xem hiện tại được hiển thị ít nhất theo tỷ lệ phần trăm được chỉ định. Mã mẫu như sau,

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

isComplempleDisplayed ()

isComplempleDisplayed () không có đối số. Nó trả về một so khớp, khớp với chế độ xem hiện đang được hiển thị hoàn toàn trên màn hình. Mã mẫu như sau,

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

được kích hoạt()

isEnabled () không có đối số. Nó trả về một trình so khớp, phù hợp với chế độ xem được bật. Mã mẫu như sau,

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

isFocusable ()

isFocusable () không có đối số. Nó trả về một trình so khớp, phù hợp với chế độ xem có tùy chọn tiêu điểm. Mã mẫu như sau,

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

hasFocus ()

hasFocus () không có đối số. Nó trả về một đối sánh, đối sánh với chế độ xem hiện đang được lấy tiêu điểm. Mã mẫu như sau,

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

isClickable ()

isClickable () không có đối số. Nó trả về một trình so khớp, phù hợp với chế độ xem là tùy chọn nhấp chuột. Mã mẫu như sau,

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

đã được chọn()

isSelected () không có đối số. Nó trả về một so khớp, khớp với chế độ xem hiện đang được chọn. Mã mẫu như sau,

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

isChecked ()

isChecked () không có đối số. Nó trả về một trình so khớp, phù hợp với dạng xem thuộc loại CompoundButton (hoặc kiểu con của nó) và ở trạng thái được kiểm tra. Mã mẫu như sau,

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

isNotChecked ()

isNotChecked () đối lập với isChecked. Mã mẫu như sau *

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

isJavascriptEnabled ()

isJavascriptEnabled () không có đối số. Nó trả về một đối sánh, đối sánh với WebView đang đánh giá JavaScript. Mã mẫu như sau,

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

với cha mẹ()

withParent () chấp nhận một đối số kiểu Matcher <View>. Đối số đề cập đến một chế độ xem. Nó trả về một đối sánh, đối sánh với chế độ xem mà chế độ xem được chỉ định là chế độ xem mẹ. Mã mẫu như sau,

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

hasSibling ()

hasSibling () chấp nhận một đối số kiểu Matcher> View <. Đối số đề cập đến một chế độ xem. Nó trả về một trình so khớp, khớp với chế độ xem mà chế độ xem được truyền vào là một trong những chế độ xem anh chị em của nó. Mã mẫu như sau,

onView(hasSibling(withId(R.id.siblingView)))

với trẻ con()

withChild () chấp nhận một đối số kiểu Matcher <View>. Đối số đề cập đến một chế độ xem. Nó trả về một đối sánh, đối sánh với chế độ xem mà chế độ xem được truyền vào là chế độ xem con. Mã mẫu như sau,

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

hasChildCount ()

hasChildCount () chấp nhận một đối số kiểu int. Đối số đề cập đến số lượng con của một chế độ xem. Nó trả về một trình so khớp, đối sánh với chế độ xem có chính xác số lượng chế độ xem con như được chỉ định trong đối số. Mã mẫu như sau,

onView(hasChildCount(4))

hasMinimumChildCount ()

hasMinimumChildCount () chấp nhận một đối số kiểu int. Đối số đề cập đến số lượng con của một chế độ xem. Nó trả về một trình so khớp, khớp với chế độ xem có ít nhất số chế độ xem con như được chỉ định trong đối số. Mã mẫu như sau,

onView(hasMinimumChildCount(4))

hasDescendant ()

hasDescendant () chấp nhận một đối số kiểu Matcher <View>. Đối số đề cập đến một chế độ xem. Nó trả về một trình so khớp, phù hợp với chế độ xem mà chế độ xem được truyền vào là một trong những chế độ xem con trong hệ thống phân cấp chế độ xem. Mã mẫu như sau,

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

isDescendantOfA ()

isDescendantOfA () chấp nhận một đối số kiểu Matcher <View>. Đối số đề cập đến một chế độ xem. Nó trả về một trình so khớp, đối sánh với chế độ xem mà chế độ xem được truyền vào là một trong những chế độ xem tổ tiên trong hệ thống phân cấp chế độ xem. Mã mẫu như sau,

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

Espresso cung cấp các tùy chọn khác nhau để tạo các trình so khớp chế độ xem tùy chỉnh của riêng chúng tôi và nó dựa trên các trình so khớp của Hamcrest . Custom matcher là một khái niệm rất mạnh mẽ để mở rộng khuôn khổ và cũng để tùy chỉnh khuôn khổ theo sở thích của chúng ta. Một số ưu điểm của việc viết các trình đối sánh tùy chỉnh như sau,

  • Để khai thác tính năng độc đáo của chế độ xem tùy chỉnh của riêng chúng tôi

  • Trình so khớp tùy chỉnh giúp trong các trường hợp thử nghiệm dựa trên AdapterView khớp với các loại dữ liệu cơ bản khác nhau.

  • Để đơn giản hóa các trình so khớp hiện tại bằng cách kết hợp các tính năng của nhiều trình so khớp

Chúng tôi có thể tạo trình đối sánh mới khi có nhu cầu và khá dễ dàng. Hãy để chúng tôi tạo một trình so khớp tùy chỉnh mới, trả về một trình so khớp để kiểm tra cả id và văn bản của TextView .

Espresso cung cấp hai lớp sau để viết các trình kết hợp mới -

  • TypeSafeMatcher

  • BoundedMatcher

Cả hai lớp đều giống nhau về bản chất ngoại trừ việc BoundMatcher xử lý trong suốt quá trình ép đối tượng thành kiểu đúng mà không cần kiểm tra thủ công để tìm đúng kiểu. Chúng tôi sẽ tạo một trình đối sánh mới, vớiIdAndText bằng cách sử dụng lớp BoundMatcher . Hãy để chúng tôi kiểm tra các bước để viết đối sánh mới.

  • Thêm phần phụ thuộc bên dưới vào tệp app / build.gradle và đồng bộ hóa nó.

dependencies {
   implementation 'androidx.test.espresso:espresso-core:3.1.1'
}
  • Tạo một lớp mới để bao gồm các đối sánh (phương thức) của chúng tôi và đánh dấu nó là cuối cùng

public final class MyMatchers {
}
  • Khai báo một phương thức tĩnh bên trong lớp mới với các đối số cần thiết và đặt Matcher <View> làm kiểu trả về.

public final class MyMatchers {
   @NonNull
   public static Matcher<View> withIdAndText(final Matcher<Integer>
   integerMatcher, final Matcher<String> stringMatcher) {
   }
}
  • Tạo một đối tượng BoundMatcher mới (cả giá trị trả về) với chữ ký bên dưới bên trong phương thức tĩnh,

public final class MyMatchers {
   @NonNull
   public static Matcher<View> withIdAndText(final Matcher<Integer>
   integerMatcher, final Matcher<String> stringMatcher) {
      return new BoundedMatcher<View, TextView>(TextView.class) {
      };
   }
}
  • Override describeTomatchesSafely phương pháp trong BoundedMatcher đối tượng. descriptionTo có một đối số duy nhất thuộc kiểu Mô tả không có kiểu trả về và nó được sử dụng để báo lỗi thông tin liên quan đến trình so khớp. MatchSafely có một đối số kiểu TextView với kiểu trả về là boolean và nó được sử dụng để khớp với dạng xem.

Phiên bản cuối cùng của mã như sau,

public final class MyMatchers {
   @NonNull
   public static Matcher<View> withIdAndText(final Matcher<Integer>
   integerMatcher, final Matcher<String> stringMatcher) {
      return new BoundedMatcher<View, TextView>(TextView.class) {
         @Override
         public void describeTo(final Description description) {
            description.appendText("error text: ");
            stringMatcher.describeTo(description);
            integerMatcher.describeTo(description);
         }
         @Override
         public boolean matchesSafely(final TextView textView) {
            return stringMatcher.matches(textView.getText().toString()) &&
            integerMatcher.matches(textView.getId());
         }
      };
   }
}
  • Cuối cùng, chúng tôi có thể sử dụng mew matcher của mình để viết trường hợp thử nghiệm như được gieo bên dưới,

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

Như đã thảo luận trước đó, xác nhận chế độ xem được sử dụng để khẳng định rằng cả chế độ xem thực tế (được tìm thấy bằng cách sử dụng trình so khớp chế độ xem) và chế độ xem mong đợi là giống nhau. Một mã mẫu như sau,

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

Đây,

  • onView () trả về đối tượng ViewInteration tương ứng với chế độ xem đã so khớp. ViewInteraction được sử dụng để tương tác với chế độ xem phù hợp.

  • withId (R.id.my_view) trả về trình so khớp chế độ xem sẽ khớp với chế độ xem (thực tế) có thuộc tính id bằng my_view .

  • withText (“Xin chào”) cũng trả về một trình so khớp chế độ xem sẽ khớp với chế độ xem (dự kiến) có các thuộc tính văn bản bằng Hello .

  • check là một phương thức chấp nhận một đối số kiểu ViewAssertion và thực hiện xác nhận bằng cách sử dụng đối tượng ViewAssertion được truyền vào .

  • đối sánh (withText (“Xin chào”)) trả về xác nhận chế độ xem, điều này sẽ thực hiệnreal jobkhẳng định rằng cả chế độ xem thực tế (được tìm thấy bằng cách sử dụng withId ) và chế độ xem mong đợi (được tìm thấy bằng cách sử dụng withText ) là một và giống nhau.

Hãy để chúng tôi tìm hiểu một số phương pháp được cung cấp bởi khung thử nghiệm cà phê espresso để khẳng định đối tượng xem.

không tồn tại()

Trả về xác nhận chế độ xem, điều này đảm bảo rằng trình đối sánh chế độ xem không tìm thấy bất kỳ chế độ xem phù hợp nào.

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

Ở đây, trường hợp kiểm tra đảm bảo rằng không có chế độ xem nào có văn bản Xin chào.

diêm()

Chấp nhận trình so khớp chế độ xem mục tiêu và trả về xác nhận chế độ xem, điều này đảm bảo rằng trình so khớp chế độ xem (thực tế) tồn tại và khớp với chế độ xem được khớp với chế độ xem mục tiêu.

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

Ở đây, trường hợp kiểm tra đảm bảo rằng chế độ xem có id, R.id.textView_hello tồn tại và khớp với chế độ xem đích có văn bản Hello World!

isBottomAlignedWith ()

Chấp nhận trình so khớp chế độ xem mục tiêu và trả về xác nhận chế độ xem, điều này đảm bảo rằng trình so khớp chế độ xem (thực tế) tồn tại và được căn chỉnh dưới cùng với trình so khớp chế độ xem mục tiêu.

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

Ở đây, trường hợp kiểm tra đảm bảo rằng chế độ xem có id, R.id.view tồn tại và được căn chỉnh dưới cùng với chế độ xem có id, R.id.target_view .

isComplempleAbove ()

Chấp nhận trình so khớp chế độ xem mục tiêu và trả về xác nhận chế độ xem, điều này đảm bảo rằng trình so khớp chế độ xem (thực tế) tồn tại và được đặt hoàn toàn phía trên trình so khớp chế độ xem mục tiêu.

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

Ở đây, trường hợp kiểm tra đảm bảo rằng chế độ xem có id, R.id.view tồn tại và được đặt hoàn toàn phía trên chế độ xem có id, R.id.target_view

isComplempleBelow ()

Chấp nhận trình so khớp chế độ xem mục tiêu và trả về xác nhận chế độ xem, điều này đảm bảo rằng trình so khớp chế độ xem (thực tế) tồn tại và được đặt hoàn toàn bên dưới trình so khớp chế độ xem mục tiêu.

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

Ở đây, trường hợp kiểm tra đảm bảo rằng chế độ xem có id, R.id.view tồn tại và được đặt hoàn toàn bên dưới chế độ xem có id, R.id.target_view .

isComplempleLeftOf ()

Chấp nhận trình so khớp chế độ xem mục tiêu và trả về xác nhận chế độ xem, điều này đảm bảo rằng trình so khớp chế độ xem (thực tế) tồn tại và được đặt hoàn toàn bên trái trình so khớp chế độ xem mục tiêu.

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

Ở đây, trường hợp kiểm tra đảm bảo rằng chế độ xem có id, R.id.view tồn tại và được đặt hoàn toàn bên trái chế độ xem có id, R.id.target_view

isComplempleRightOf ()

Chấp nhận trình so khớp chế độ xem mục tiêu và trả về xác nhận chế độ xem, điều này đảm bảo rằng trình so khớp chế độ xem (thực tế) tồn tại và được định vị hoàn toàn bên phải của trình so khớp chế độ xem mục tiêu.

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

Ở đây, trường hợp kiểm tra đảm bảo rằng chế độ xem có id, R.id.view tồn tại và được đặt hoàn toàn bên phải chế độ xem có id, R.id.target_view.

isLeftAlignedWith ()

Chấp nhận trình so khớp chế độ xem mục tiêu và trả về xác nhận chế độ xem, điều này đảm bảo rằng trình so khớp chế độ xem (thực tế) tồn tại và được căn trái với trình so khớp chế độ xem mục tiêu.

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

Ở đây, trường hợp kiểm tra đảm bảo rằng chế độ xem có id, R.id.view tồn tại và được căn trái với chế độ xem có id, R.id.target_view

isPartiallyAbove ()

Chấp nhận trình so khớp chế độ xem mục tiêu và trả về xác nhận chế độ xem, điều này đảm bảo rằng trình so khớp chế độ xem (thực tế) tồn tại và được đặt một phần phía trên trình so khớp chế độ xem mục tiêu.

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

Ở đây, trường hợp kiểm tra đảm bảo rằng chế độ xem có id, R.id.view tồn tại và được đặt một phần phía trên chế độ xem có id, R.id.target_view

isPartiallyBelow ()

Chấp nhận trình đối sánh chế độ xem mục tiêu và trả về xác nhận chế độ xem, điều này đảm bảo rằng trình đối sánh chế độ xem (thực tế) tồn tại và được đặt một phần bên dưới trình đối sánh chế độ xem mục tiêu.

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

Ở đây, trường hợp kiểm tra đảm bảo rằng chế độ xem có id, R.id.view tồn tại và được đặt một phần bên dưới chế độ xem có id, R.id.target_view .

isPartiallyLeftOf ()

Chấp nhận trình so khớp chế độ xem mục tiêu và trả về xác nhận chế độ xem, điều này đảm bảo rằng trình so khớp chế độ xem (thực tế) tồn tại và được định vị một phần bên trái của trình so khớp chế độ xem mục tiêu.

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

Ở đây, trường hợp kiểm tra đảm bảo rằng chế độ xem có id, R.id.view tồn tại và được định vị một phần bên trái của chế độ xem có id, R.id.target_view .

isPartiallyRightOf ()

Chấp nhận trình so khớp chế độ xem mục tiêu và trả về xác nhận chế độ xem, điều này đảm bảo rằng trình so khớp chế độ xem (thực tế) tồn tại và được định vị một phần bên phải của trình so khớp chế độ xem mục tiêu

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

Ở đây, trường hợp kiểm tra đảm bảo rằng chế độ xem có id, R.id.view tồn tại và được định vị một phần bên phải của chế độ xem có id, R.id.target_view .

isRightAlignedWith ()

Chấp nhận trình so khớp chế độ xem mục tiêu và trả về xác nhận chế độ xem, điều này đảm bảo rằng trình so khớp chế độ xem (thực tế) tồn tại và được căn chỉnh đúng với trình so khớp chế độ xem mục tiêu.

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

Ở đây, trường hợp kiểm tra đảm bảo rằng chế độ xem có id, R.id.view tồn tại và được căn chỉnh phù hợp với chế độ xem có id, R.id.target_view .

isTopAlignedWith ()

Chấp nhận trình so khớp chế độ xem mục tiêu và trả về xác nhận chế độ xem, điều này đảm bảo rằng trình so khớp chế độ xem (thực tế) tồn tại và được căn chỉnh trên cùng với trình so khớp chế độ xem mục tiêu.

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

Ở đây, trường hợp kiểm tra đảm bảo rằng chế độ xem có id, R.id.view tồn tại và được căn chỉnh trên cùng với chế độ xem có id, R.id.target_view

noEllipsizedText ()

Trả về một xác nhận dạng xem, điều này đảm bảo rằng hệ thống phân cấp dạng xem không chứa dạng xem văn bản được chia nhỏ hoặc bị cắt bỏ.

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

noMultilineButtons ()

Trả về xác nhận dạng xem, đảm bảo rằng phân cấp dạng xem không chứa các nút nhiều dòng.

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

noOverlaps ()

Trả về xác nhận chế độ xem, điều này đảm bảo rằng đối tượng con có thể gán cho TextView hoặc ImageView không chồng chéo lên nhau. Nó có một tùy chọn khác, chấp nhận trình đối sánh chế độ xem mục tiêu và trả về xác nhận chế độ xem, điều này đảm bảo rằng chế độ xem con cháu khớp với chế độ xem mục tiêu không chồng chéo.

Như đã học trước đó, các thao tác xem tự động hóa tất cả các hành động có thể mà người dùng có thể thực hiện trong ứng dụng Android. Espresso onView và “onData” cung cấp phương thức thực hiện , chấp nhận các hành động xem và gọi / tự động hóa các hành động tương ứng của người dùng trong môi trường thử nghiệm. Ví dụ: “click ()” là một hành động xem, khi được chuyển đến phương thức onView ( R.id.myButton ) .perform (click ()) , sẽ kích hoạt sự kiện nhấp của nút (với id: “myButton” ) trong môi trường thử nghiệm.

Trong chương này, chúng ta hãy tìm hiểu về các thao tác xem được cung cấp bởi khung thử nghiệm cà phê espresso.

typeText ()

typeText () chấp nhận một đối số (văn bản) kiểu String và trả về một hành động dạng xem. Hành động dạng xem trả về nhập văn bản được cung cấp vào dạng xem. Trước khi đặt văn bản, nó chạm vào chế độ xem một lần. Nội dung có thể được đặt ở vị trí tùy ý nếu nó đã chứa văn bản.

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

typeTextIntoFocusedView ()

typeTextIntoFocusedView () tương tự như typeText () ngoại trừ việc nó đặt văn bản ngay bên cạnh vị trí con trỏ trong dạng xem.

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

ReplaceText ()

ReplaceText () tương tự như typeText () ngoại trừ việc nó thay thế nội dung của khung nhìn.

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

clearText ()

clearText () không có đối số và trả về một hành động dạng xem, hành động này sẽ xóa văn bản trong dạng xem.

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

pressKey ()

pressKey () chấp nhận mã khóa (ví dụ KeyEvent.KEYCODE_ENTER) và trả về một hành động xem, hành động này sẽ nhấn phím tương ứng với mã khóa.

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

pressMenuKey ()

pressMenuKey () không có đối số và trả về một hành động xem, hành động này sẽ nhấn phím menu phần cứng.

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

closeSoftKeyboard ()

closeSoftKeyboard () không có đối số và trả về một hành động xem, hành động này sẽ đóng bàn phím, nếu một hành động được mở.

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

nhấp chuột()

click () không có đối số và trả về một hành động dạng xem, hành động này sẽ gọi hành động bấm của dạng xem.

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

nhấn đúp chuột()

doubleClick () không có đối số và trả về một hành động xem, hành động này sẽ gọi hành động nhấp đúp vào chế độ xem.

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

longClick ()

longClick () không có đối số và trả về một hành động xem, hành động này sẽ gọi hành động nhấp dài của chế độ xem.

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

pressBack ()

pressBack () không có đối số và trả về một hành động xem, hành động này sẽ nhấp vào nút quay lại.

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

pressBackUnconditionally ()

pressBackUnconditionally () không có đối số và trả về một hành động xem, hành động này sẽ nhấp vào nút quay lại và không đưa ra ngoại lệ nếu hành động nút quay lại thoát khỏi chính ứng dụng.

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

mở liên kết()

openLink () có hai đối số. Đối số đầu tiên (liên kết văn bản) là loại Matcher và đề cập nội dung của thẻ HTML neo. Đối số thứ hai (url) thuộc loại Matcher và tham chiếu đến url của thẻ liên kết HTML. Nó chỉ áp dụng cho TextView . Nó trả về một hành động xem, thu thập tất cả các thẻ liên kết HTML có sẵn trong nội dung của chế độ xem văn bản, tìm thẻ liên kết khớp với đối số đầu tiên (văn bản liên kết) và đối số thứ hai (url) và cuối cùng mở url tương ứng. Chúng ta hãy coi một chế độ xem văn bản có nội dung là -

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

Sau đó, liên kết có thể được mở và kiểm tra bằng cách sử dụng trường hợp thử nghiệm bên dưới,

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

Tại đây, openLink sẽ lấy nội dung của dạng xem văn bản, tìm liên kết có bản quyền dưới dạng văn bản, www.google.com dưới dạng url và mở url trong trình duyệt.

openLinkWithText ()

openLinkWithText () có một đối số, có thể là kiểu ** String * hoặc Matcher. Nó chỉ đơn giản là một đoạn ngắn của phương thức openLink *.

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

openLinkWithUri ()

openLinkWithUri () có một đối số, có thể là kiểu String hoặc Matcher. Nó chỉ đơn giản là một đoạn ngắn của phương thức openLink *.

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

pressImeActionButton ()

pressImeActionButton () không có đối số và trả về một hành động dạng xem, hành động này sẽ thực thi hành động được đặt trong cấu hình android: imeOptions . Ví dụ: nếu android: imeOptions bằng actionNext, điều này sẽ di chuyển con trỏ đến dạng xem EditText có thể tiếp theo trong màn hình.

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

scrollTo ()

scrollTo () không có đối số và trả về một hành động xem, hành động này sẽ cuộn scrollView phù hợp trên màn hình.

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

vuốt xuống()

vuốtDown () không có đối số và trả về một hành động xem, hành động này sẽ kích hoạt hành động vuốt xuống trên màn hình.

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

Kéo mạnh lên()

vuốtUp () không có đối số và trả về một hành động xem, hành động này sẽ kích hoạt hành động vuốt lên trên màn hình.

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

vuốt sang phải()

vuốtRight () không có đối số và trả về một hành động xem, hành động này sẽ kích hoạt hành động vuốt sang phải trên màn hình.

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

vuốtLeft ()

vuốtLeft () không có đối số và trả về một hành động xem, hành động này sẽ kích hoạt hành động vuốt sang trái trên màn hình.

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

AdapterView là một loại chế độ xem đặc biệt được thiết kế đặc biệt để hiển thị tập hợp thông tin tương tự như danh sách sản phẩm và danh bạ người dùng được tìm nạp từ nguồn dữ liệu cơ bản bằng Bộ điều hợp . Nguồn dữ liệu có thể là danh sách đơn giản đến các mục nhập cơ sở dữ liệu phức tạp. Một số chế độ xem có nguồn gốc từ AdapterViewListView , GridViewSpinner .

AdapterView hiển thị giao diện người dùng động tùy thuộc vào lượng dữ liệu có sẵn trong nguồn dữ liệu cơ bản. Ngoài ra, AdapterView chỉ hiển thị dữ liệu cần thiết tối thiểu, có thể được hiển thị trong vùng hiển thị có sẵn của màn hình. AdapterView thực hiện điều này để tiết kiệm bộ nhớ và làm cho giao diện người dùng trông mượt mà ngay cả khi dữ liệu bên dưới lớn.

Sau khi phân tích, bản chất của kiến trúc AdapterView làm cho tùy chọn onView và các trình đối sánh chế độ xem của nó không liên quan vì chế độ xem cụ thể được kiểm tra có thể không được hiển thị ngay từ đầu. May mắn thay, espresso cung cấp một phương thức, onData ( ), chấp nhận các trình so khớp hamcrest (có liên quan đến kiểu dữ liệu của dữ liệu cơ bản) để khớp với dữ liệu cơ bản và trả về đối tượng kiểu DataInteraction tương ứng với chế độ xem o dữ liệu đã khớp. Một mã mẫu như sau,

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

Ở đây, onData () khớp với mục nhập “Apple”, nếu nó có sẵn trong dữ liệu cơ bản (danh sách mảng) và trả về đối tượng DataInteraction để tương tác với chế độ xem phù hợp (TextView tương ứng với mục nhập “Apple”).

Phương pháp

DataInteraction cung cấp các phương pháp dưới đây để tương tác với chế độ xem,

biểu diễn()

Điều này chấp nhận các hành động xem và kích hoạt các hành động đã chuyển trong chế độ xem.

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

kiểm tra()

Điều này chấp nhận các xác nhận chế độ xem và kiểm tra các xác nhận chế độ xem đã chuyển.

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

inAdapterView ()

Điều này chấp nhận đối sánh lượt xem. Nó chọn AdapterView cụ thể dựa trên đối sánh chế độ xem được truyền vào và trả về đối tượng DataInteraction để tương tác với AdapterView phù hợp

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

atPosition ()

Điều này chấp nhận một đối số kiểu số nguyên và đề cập đến vị trí của mục trong dữ liệu cơ bản. Nó chọn chế độ xem tương ứng với giá trị vị trí được truyền vào của dữ liệu và trả về đối tượng DataInteraction để tương tác với chế độ xem phù hợp. Nó sẽ hữu ích, nếu chúng ta biết thứ tự chính xác của dữ liệu cơ bản.

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

onChildView ()

Điều này chấp nhận đối sánh chế độ xem và đối sánh chế độ xem bên trong chế độ xem con cụ thể. Ví dụ: chúng tôi có thể tương tác với các mặt hàng cụ thể như nút Mua trong AdapterView dựa trên danh sách sản phẩm .

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

Viết đơn đăng ký mẫu

Làm theo các bước được hiển thị bên dưới để viết một ứng dụng đơn giản dựa trên AdapterView và viết một trường hợp thử nghiệm bằng phương thức onData () .

  • Khởi động Android studio.

  • Tạo dự án mới như đã thảo luận trước đó và đặt tên là MyFruitApp .

  • Di chuyển ứng dụng sang khung AndroidX bằng RefactorDi chuyển sang menu tùy chọn AndroidX .

  • Loại bỏ thiết kế mặc định trong hoạt động chính và thêm ListView . Nội dung của activity_main.xml như sau,

<?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/listView"
      android:layout_width = "wrap_content"
      android:layout_height = "wrap_content" />
</RelativeLayout>
  • Thêm tài nguyên bố cục mới, item.xml để chỉ định mẫu mục của dạng xem danh sách. Nội dung của item.xml như sau,

<?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"
/>
  • Bây giờ, hãy tạo một bộ điều hợp có mảng trái cây làm dữ liệu cơ bản và đặt nó ở chế độ xem danh sách. Điều này cần được thực hiện trong onCreate () của MainActivity như được chỉ định bên dưới,

@Override
protected void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   setContentView(R.layout.activity_main);
   
   // Find fruit list view
   final ListView listView = (ListView) findViewById(R.id.listView);
   
   // Initialize 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 list 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 list view
   listView.setAdapter(adapter);
}
  • Bây giờ, hãy biên dịch mã và chạy ứng dụng. Ảnh chụp màn hình của My Fruit App như sau,

  • Bây giờ, hãy mở tệp ExampleI Kinh nguyệtTest.java và thêm ActivityTestRule như được chỉ định bên dưới,

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

Ngoài ra, hãy đảm bảo rằng cấu hình thử nghiệm được thực hiện trong 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'
}
  • Thêm một trường hợp thử nghiệm mới để kiểm tra chế độ xem danh sách như bên dưới,

@Test
public void listView_isCorrect() {
   // check list view is visible
   onView(withId(R.id.listView)).check(matches(isDisplayed()));
   onData(allOf(is(instanceOf(String.class)), startsWith("Apple"))).perform(click());
   onData(allOf(is(instanceOf(String.class)), startsWith("Apple")))
      .check(matches(withText("Apple")));
   // click a child item
   onData(allOf())
      .inAdapterView(withId(R.id.listView))
      .atPosition(10)
      .perform(click());
}
  • Cuối cùng, chạy test case bằng menu ngữ cảnh của android studio và kiểm tra xem tất cả test case có thành công hay không.

WebView là một dạng xem đặc biệt do android cung cấp để hiển thị các trang web bên trong ứng dụng. WebView không cung cấp tất cả các tính năng của một ứng dụng trình duyệt chính thức như chrome và firefox. Tuy nhiên, nó cung cấp quyền kiểm soát hoàn toàn đối với nội dung được hiển thị và hiển thị tất cả các tính năng android được gọi bên trong các trang web. Nó cho phép WebView và cung cấp một môi trường đặc biệt, nơi có thể dễ dàng thiết kế giao diện người dùng bằng công nghệ HTML và các tính năng gốc như camera và quay số một số liên lạc. Bộ tính năng này cho phép WebView cung cấp một loại ứng dụng mới được gọi là ứng dụng Kết hợp , trong đó giao diện người dùng được thực hiện bằng HTML và logic nghiệp vụ được thực hiện bằng JavaScript hoặc thông qua điểm cuối API bên ngoài.

Thông thường, việc thử nghiệm một WebView cần phải là một thách thức vì nó sử dụng công nghệ HTML cho các phần tử giao diện người dùng chứ không phải giao diện / chế độ xem người dùng gốc. Espresso vượt trội trong lĩnh vực này bằng cách cung cấp một tập hợp mới cho các trình so khớp web và xác nhận web, tương tự như các trình so khớp chế độ xem gốc và xác nhận chế độ xem. Đồng thời, nó cung cấp một cách tiếp cận cân bằng bằng cách bao gồm một môi trường thử nghiệm dựa trên công nghệ web.

Web Espresso được xây dựng dựa trên khung WebDriver Atom, được sử dụng để tìm và thao tác các phần tử web. Atom tương tự như các hành động xem. Atom sẽ thực hiện tất cả các tương tác bên trong một trang web. WebDriver hiển thị một tập hợp các phương thức được xác định trước, như findElement () , getElement () để tìm các phần tử web và trả về các nguyên tử tương ứng (để thực hiện hành động trong trang web).

Câu lệnh kiểm tra web chuẩn trông giống như đoạn mã dưới đây,

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

Đây,

  • onWebView () - Tương tự như onView (), nó cho thấy một bộ API để kiểm tra một WebView.

  • withElement () - Một trong một số phương thức được sử dụng để định vị các phần tử web bên trong trang web bằng cách sử dụng Atom và trả về đối tượng WebInteration, tương tự như ViewInteraction.

  • performance () - Thực thi hành động bên trong trang web bằng Atom và trả về WebInteraction.

  • check () - Điều này thực hiện xác nhận cần thiết bằng cách sử dụng WebAssertion.

Mã thử nghiệm web mẫu như sau,

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

Đây,

  • findElement () định vị một phần tử và trả về một Atom

  • webMatches tương tự như phương pháp so khớp

Viết đơn đăng ký mẫu

Hãy để chúng tôi viết một ứng dụng đơn giản dựa trên WebView và viết một trường hợp thử nghiệm bằng phương thức onWebView () . Làm theo các bước sau để viết một ứng dụng mẫu -

  • Khởi động Android studio.

  • Tạo dự án mới như đã thảo luận trước đó và đặt tên là MyWebViewApp .

  • Di chuyển ứng dụng sang khung AndroidX bằng RefactorDi chuyển sang menu tùy chọn AndroidX .

  • Thêm tùy chọn cấu hình bên dưới vào tệp AndroidManifest.xml để cấp quyền truy cập Internet.

<uses-permission android:name = "android.permission.INTERNET" />
  • Web Espresso được cung cấp dưới dạng một plugin riêng biệt. Vì vậy, hãy thêm phần phụ thuộc vào app / build.gradle và đồng bộ hóa nó.

dependencies {
   androidTestImplementation 'androidx.test:rules:1.1.1'
   androidTestImplementation 'androidx.test.espresso:espresso-web:3.1.1'
}
  • Loại bỏ thiết kế mặc định trong hoạt động chính và thêm WebView. Nội dung của activity_main.xml như sau,

<?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>
  • Tạo một lớp mới, ExtendedWebViewClient mở rộng WebViewClient và ghi đè phương thức shouldOverrideUrlLoading để tải hành động liên kết trong cùng một WebView ; nếu không, nó sẽ mở một cửa sổ trình duyệt mới bên ngoài ứng dụng. Đặt nó trong MainActivity.java .

private class ExtendedWebViewClient extends WebViewClient {
   @Override
   public boolean shouldOverrideUrlLoading(WebView view, String url) {
      view.loadUrl(url);
      return true;
   }
}
  • Bây giờ, hãy thêm đoạn mã dưới đây vào phương thức onCreate của MainActivity . Mục đích của mã là tìm WebView , định cấu hình đúng cách và sau đó tải url đích.

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

// set web view client
webView.setWebViewClient(new ExtendedWebViewClient());

// Clear cache
webView.clearCache(true);

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

Đây,

  • Nội dung của index.html như sau:

<html>
   <head>
      <title>Android Web View Sample</title>
   </head>
   <body>
      <h1>Fruits</h1>
      <ol>
         <li><a href = "apple.html" id = "apple">Apple</a></li>
         <li><a href = "banana.html" id = "banana">Banana</a></li>
         </ol>
   </body>
</html>
  • Nội dung của tệp apple.html được tham chiếu trong index.html như sau:

<html>
   <head>
      <title>Android Web View Sample</title>
   </head>
   
   <body>
      <h1>Apple</h1>
   </body>
</html>
  • Nội dung của tệp banana.html được tham chiếu trong banana.html như sau,

<html>
   <head>
      <title>Android Web View Sample</title>
   </head>
   
   <body>
      <h1>Banana</h1>
   </body>
</html>
  • Đặt index.html, apple.html và banana.html trong máy chủ web

  • Thay thế url trong phương thức loadUrl bằng url đã định cấu hình của bạn.

  • Bây giờ, hãy chạy ứng dụng và kiểm tra thủ công xem mọi thứ có ổn không. Dưới đây là ảnh chụp màn hình của ứng dụng mẫu WebView -

  • Bây giờ, hãy mở tệp ExampleIusalmentedTest.java và thêm quy tắc bên dưới:

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

Tại đây, chúng tôi đã tìm thấy WebView và JavaScript được bật của WebView vì khung thử nghiệm web espresso hoạt động độc quyền thông qua công cụ JavaScript để xác định và thao tác phần tử web.

  • Bây giờ, hãy thêm trường hợp thử nghiệm để kiểm tra WebView của chúng tôi và hành vi của nó.

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

Ở đây, thử nghiệm được thực hiện theo thứ tự sau,

  • đã tìm thấy liên kết, apple sử dụng thuộc tính id của nó thông qua phương thức findElement ()kiểu liệt kê Locator.ID .

  • kiểm tra văn bản của liên kết bằng phương thức webMatches ()

  • thực hiện hành động nhấp vào liên kết. Nó mở ra trang apple.html .

  • lại tìm thấy phần tử h1 bằng phương thức findElement () và kiểu liệt kê Locator.TAG_NAME .

  • cuối cùng kiểm tra lại văn bản của thẻ h1 bằng phương thức webMatches () .

  • Cuối cùng, chạy test case bằng menu ngữ cảnh của studio android.

Trong chương này, chúng ta sẽ học cách kiểm tra các hoạt động không đồng bộ bằng cách sử dụng Espresso Idling Resources.

Một trong những thách thức của ứng dụng hiện đại là cung cấp trải nghiệm người dùng mượt mà. Cung cấp trải nghiệm người dùng mượt mà bao gồm nhiều công việc trong nền để đảm bảo rằng quá trình ứng dụng không mất nhiều thời gian hơn vài mili giây. Nhiệm vụ nền có phạm vi từ nhiệm vụ đơn giản đến tốn kém và phức tạp là tìm nạp dữ liệu từ API / cơ sở dữ liệu từ xa. Để đối mặt với thách thức trước đây, một nhà phát triển đã từng viết tác vụ tốn kém và chạy lâu trong một chuỗi nền và đồng bộ hóa với UIThread chính sau khi chuỗi nền hoàn thành.

Nếu việc phát triển một ứng dụng đa luồng đã phức tạp, thì việc viết các trường hợp kiểm thử cho nó còn phức tạp hơn. Ví dụ: chúng ta không nên kiểm tra một AdapterView trước khi dữ liệu cần thiết được tải từ cơ sở dữ liệu. Nếu việc tìm nạp dữ liệu được thực hiện trong một luồng riêng biệt, việc kiểm tra cần phải đợi cho đến khi luồng hoàn thành. Vì vậy, môi trường thử nghiệm nên được đồng bộ hóa giữa luồng nền và luồng giao diện người dùng. Espresso cung cấp sự hỗ trợ tuyệt vời để thử nghiệm ứng dụng đa luồng. Một ứng dụng sử dụng chuỗi theo những cách sau và espresso hỗ trợ mọi tình huống.

Phân luồng giao diện người dùng

Nó được sử dụng nội bộ bởi Android SDK để cung cấp trải nghiệm người dùng mượt mà với các phần tử giao diện người dùng phức tạp. Espresso hỗ trợ kịch bản này một cách minh bạch và không cần bất kỳ cấu hình và mã hóa đặc biệt nào.

Nhiệm vụ không đồng bộ

Các ngôn ngữ lập trình hiện đại hỗ trợ lập trình không đồng bộ để thực hiện phân luồng nhẹ mà không cần lập trình luồng phức tạp. Tác vụ async cũng được hỗ trợ một cách minh bạch bởi khuôn khổ espresso.

Chủ đề người dùng

Một nhà phát triển có thể bắt đầu một chuỗi mới để tìm nạp dữ liệu phức tạp hoặc lớn từ cơ sở dữ liệu. Để hỗ trợ tình huống này, espresso cung cấp khái niệm tài nguyên chạy không tải.

Hãy sử dụng tìm hiểu khái niệm tài nguyên không tải và cách sử dụng nó trong chương này.

Tổng quat

Khái niệm về tài nguyên chạy không tải rất đơn giản và trực quan. Ý tưởng cơ bản là tạo một biến (giá trị boolean) bất cứ khi nào một quá trình chạy dài được bắt đầu trong một luồng riêng biệt để xác định xem quá trình có đang chạy hay không và đăng ký nó trong môi trường thử nghiệm. Trong quá trình thử nghiệm, người chạy thử nghiệm sẽ kiểm tra biến đã đăng ký, nếu có và sau đó tìm trạng thái đang chạy của nó. Nếu trạng thái đang chạy là đúng, người chạy thử nghiệm sẽ đợi cho đến khi trạng thái trở thành sai.

Espresso cung cấp một giao diện, IdlingResources nhằm mục đích duy trì trạng thái đang chạy. Phương thức chính để triển khai là isIdleNow (). Nếu isIdleNow () trả về true, espresso sẽ tiếp tục quá trình thử nghiệm hoặc nếu không, hãy đợi cho đến khi isIdleNow () trả về false. Chúng ta cần triển khai IdlingResources và sử dụng lớp dẫn xuất. Espresso cũng cung cấp một số triển khai IdlingResources tích hợp sẵn để giảm bớt khối lượng công việc của chúng tôi. Chúng như sau,

CountingIdlingResource

Điều này duy trì một bộ đếm bên trong của tác vụ đang chạy. Nó hiển thị các phương thức tăng ()giảm () . tăng () thêm một vào bộ đếm và giảm () xóa một khỏi bộ đếm. isIdleNow () chỉ trả về true khi không có tác vụ nào hoạt động.

UriIdlingResource

Điều này tương tự như CounintIdlingResource ngoại trừ việc bộ đếm cần bằng 0 trong thời gian dài để tính cả độ trễ mạng.

IdlingThreadPoolExecutor

Đây là một triển khai tùy chỉnh của ThreadPoolExecutor để duy trì tác vụ chạy số đang hoạt động trong nhóm luồng hiện tại.

IdlingSchedonedThreadPoolExecutor

Điều này tương tự như IdlingThreadPoolExecutor , nhưng nó cũng lên lịch một tác vụ và triển khai tùy chỉnh của SchedisedThreadPoolExecutor .

Nếu bất kỳ triển khai nào ở trên của IdlingResources hoặc tùy chỉnh được sử dụng trong ứng dụng, chúng tôi cũng cần đăng ký nó vào môi trường thử nghiệm trước khi thử nghiệm ứng dụng bằng lớp IdlingRegistry như bên dưới,

IdlingRegistry.getInstance().register(MyIdlingResource.getIdlingResource());

Hơn nữa, nó có thể bị xóa sau khi hoàn tất quá trình kiểm tra như bên dưới -

IdlingRegistry.getInstance().unregister(MyIdlingResource.getIdlingResource());

Espresso cung cấp chức năng này trong một gói riêng biệt và gói này cần được định cấu hình như bên dưới trong app.gradle.

dependencies {
   implementation 'androidx.test.espresso:espresso-idling-resource:3.1.1'
   androidTestImplementation "androidx.test.espresso.idling:idlingconcurrent:3.1.1"
}

Ứng dụng mẫu

Hãy để chúng tôi tạo một ứng dụng đơn giản để liệt kê các thành quả bằng cách tìm nạp nó từ một dịch vụ web trong một chuỗi riêng và sau đó, kiểm tra nó bằng cách sử dụng khái niệm tài nguyên không tải.

  • Khởi động Android studio.

  • Tạo dự án mới như đã thảo luận trước đó và đặt tên cho nó, MyIdlingFruitApp

  • Di chuyển ứng dụng sang khung AndroidX bằng Refactor → Di chuyển sang menu tùy chọn AndroidX .

  • Thêm thư viện tài nguyên chạy không tải espresso trong app / build.gradle (và đồng bộ hóa nó) như được chỉ định bên dưới,

dependencies {
   implementation 'androidx.test.espresso:espresso-idling-resource:3.1.1'
   androidTestImplementation "androidx.test.espresso.idling:idlingconcurrent:3.1.1"
}
  • Loại bỏ thiết kế mặc định trong hoạt động chính và thêm ListView. Nội dung của activity_main.xml như sau,

<?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/listView"
      android:layout_width = "wrap_content"
      android:layout_height = "wrap_content" />
</RelativeLayout>
  • Thêm tài nguyên bố cục mới, item.xml để chỉ định mẫu mục của dạng xem danh sách. Nội dung của item.xml như sau,

<?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"
/>
  • Tạo một lớp mới - MyIdlingResource . MyIdlingResource được sử dụng để giữ IdlingResource của chúng tôi ở một nơi và tìm nạp bất cứ khi nào cần thiết. Chúng tôi sẽ sử dụng CountingIdlingResource trong ví dụ của chúng tôi.

package com.tutorialspoint.espressosamples.myidlingfruitapp;
import androidx.test.espresso.IdlingResource;
import androidx.test.espresso.idling.CountingIdlingResource;

public class MyIdlingResource {
   private static CountingIdlingResource mCountingIdlingResource =
      new CountingIdlingResource("my_idling_resource");
   public static void increment() {
      mCountingIdlingResource.increment();
   }
   public static void decrement() {
      mCountingIdlingResource.decrement();
   }
   public static IdlingResource getIdlingResource() {
      return mCountingIdlingResource;
   }
}
  • Khai báo một biến toàn cục, mIdlingResource thuộc loại CountingIdlingResource trong lớp MainActivity như bên dưới,

@Nullable
private CountingIdlingResource mIdlingResource = null;
  • Viết một phương thức riêng để lấy danh sách trái cây từ web như bên dưới,

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 line;
         StringBuffer response = new StringBuffer();
         while ((line = in.readLine()) != null) {
            response.append(line);
         }
         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;
}
  • Tạo một tác vụ mới trong phương thức onCreate () để tìm nạp dữ liệu từ web bằng phương thức getFruitList của chúng tôi, tiếp theo là tạo bộ điều hợp mới và đặt nó ở chế độ xem danh sách. Ngoài ra, giảm tài nguyên chạy không tải khi công việc của chúng ta hoàn thành trong luồng. Mã như sau,

// Get data
class FruitTask implements Runnable {
   ListView listView;
   CountingIdlingResource idlingResource;
   FruitTask(CountingIdlingResource idlingRes, ListView listView) {
      this.listView = listView;
      this.idlingResource = idlingRes;
   }
   public 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
               public void run() {
                  // Create adapter and set it to list view
                  final ArrayAdapter adapter = new
                     ArrayAdapter(MainActivity.this, R.layout.item, fruitList);
                  ListView listView = (ListView)findViewById(R.id.listView);
                  listView.setAdapter(adapter);
               }
            });
         }
      } catch (Exception e) {
         e.printStackTrace();
      }
      if (!MyIdlingResource.getIdlingResource().isIdleNow()) {
         MyIdlingResource.decrement(); // Set app as idle.
      }
   }
}

Ở đây, url trái cây được coi là http: // <miền của bạn hoặc IP / fruit.json và nó được định dạng là JSON. Nội dung như sau,

[ 
   {
      "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"
   }
]

Note - Đặt tệp vào máy chủ web cục bộ của bạn và sử dụng nó.

  • Bây giờ, tìm chế độ xem, tạo một luồng mới bằng cách chuyển FruitTask , tăng tài nguyên chạy không tải và cuối cùng bắt đầu tác vụ.

// Find list view
ListView listView = (ListView) findViewById(R.id.listView);
Thread fruitTask = new Thread(new FruitTask(this.mIdlingResource, listView));
MyIdlingResource.increment();
fruitTask.start();
  • Mã hoàn chỉnh của MainActivity như sau,

package com.tutorialspoint.espressosamples.myidlingfruitapp;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.appcompat.app.AppCompatActivity;
import androidx.test.espresso.idling.CountingIdlingResource;

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;

public class MainActivity extends AppCompatActivity {
   @Nullable
   private CountingIdlingResource mIdlingResource = null;
   @Override
   protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);
      
      // Get data
      class FruitTask implements Runnable {
         ListView listView;
         CountingIdlingResource idlingResource;
         FruitTask(CountingIdlingResource idlingRes, ListView listView) {
            this.listView = listView;
            this.idlingResource = idlingRes;
         }
         public 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
                     public void run() {
                        // Create adapter and set it to list view
                        final ArrayAdapter adapter = new ArrayAdapter(
                           MainActivity.this, R.layout.item, fruitList);
                        ListView listView = (ListView) findViewById(R.id.listView);
                        listView.setAdapter(adapter);
                     }
                  });
               }
            } catch (Exception e) {
               e.printStackTrace();
            }
            if (!MyIdlingResource.getIdlingResource().isIdleNow()) {
               MyIdlingResource.decrement(); // Set app as idle.
            }
         }
      }
      // Find list view
      ListView listView = (ListView) findViewById(R.id.listView);
      Thread fruitTask = new Thread(new FruitTask(this.mIdlingResource, listView));
      MyIdlingResource.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 line;
            StringBuffer response = new StringBuffer();
            while ((line = in.readLine()) != null) {
               response.append(line);
            }
            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;
   }
}
  • Bây giờ, hãy thêm cấu hình bên dưới vào tệp kê khai ứng dụng, AndroidManifest.xml

<uses-permission android:name = "android.permission.INTERNET" />
  • Bây giờ, hãy biên dịch đoạn mã trên và chạy ứng dụng. Ảnh chụp màn hình của My Idling Fruit App như sau,

  • Bây giờ, hãy mở tệp ExampleInticmentedTest.java và thêm ActivityTestRule như được chỉ định bên dưới,

@Rule
public 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-idling-resource:3.1.1'
   androidTestImplementation "androidx.test.espresso.idling:idlingconcurrent:3.1.1"
}
  • Thêm một trường hợp thử nghiệm mới để kiểm tra chế độ xem danh sách như bên dưới,

@Before
public void registerIdlingResource() {
   IdlingRegistry.getInstance().register(MyIdlingResource.getIdlingResource());
}
@Test
public void contentTest() {
   // click a child item
   onData(allOf())
   .inAdapterView(withId(R.id.listView))
   .atPosition(10)
   .perform(click());
}
@After
public void unregisterIdlingResource() {
   IdlingRegistry.getInstance().unregister(MyIdlingResource.getIdlingResource());
}
  • Cuối cùng, chạy test case bằng menu ngữ cảnh của android studio và kiểm tra xem tất cả test case có thành công hay không.

Android Intent được sử dụng để mở hoạt động mới, nội bộ (mở màn hình chi tiết sản phẩm từ màn hình danh sách sản phẩm) hoặc bên ngoài (như mở trình quay số để thực hiện cuộc gọi). Hoạt động có ý định nội bộ được xử lý minh bạch bởi khung thử nghiệm cà phê espresso và nó không cần bất kỳ công việc cụ thể nào từ phía người dùng. Tuy nhiên, việc gọi hoạt động bên ngoài thực sự là một thách thức vì nó vượt ra khỏi phạm vi của chúng tôi, ứng dụng đang được thử nghiệm. Một khi người dùng gọi một ứng dụng bên ngoài và thoát ra khỏi ứng dụng đang được kiểm tra, thì cơ hội người dùng quay lại ứng dụng với chuỗi hành động được xác định trước sẽ ít hơn. Do đó, chúng tôi cần giả định hành động của người dùng trước khi thử nghiệm ứng dụng. Espresso cung cấp hai tùy chọn để xử lý tình huống này. Chúng như sau,

dự định

Điều này cho phép người dùng đảm bảo rằng mục đích chính xác được mở từ ứng dụng đang được thử nghiệm.

dự định

Điều này cho phép người dùng mô phỏng một hoạt động bên ngoài như chụp ảnh từ máy ảnh, quay số từ danh sách liên hệ, v.v. và quay lại ứng dụng với bộ giá trị được xác định trước (như hình ảnh xác định trước từ máy ảnh thay vì hình ảnh thực tế) .

Thiết lập

Espresso hỗ trợ tùy chọn ý định thông qua thư viện plugin và thư viện cần được định cấu hình trong tệp gradle của ứng dụng. Tùy chọn cấu hình như sau,

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

dự định ()

Plugin mục đích Espresso cung cấp các đối sánh đặc biệt để kiểm tra xem mục đích được gọi có phải là mục đích mong đợi hay không. Các trình kết hợp được cung cấp và mục đích của các trình kết hợp như sau,

hasAction

Điều này chấp nhận hành động ý định và trả về một đối sánh phù hợp với ý định đã chỉ định.

hasData

Điều này chấp nhận dữ liệu và trả về một đối sánh, khớp với dữ liệu được cung cấp cho ý định trong khi gọi nó.

đóng gói

Điều này chấp nhận tên gói ý định và trả về một đối sánh, phù hợp với tên gói của ý định được gọi.

Bây giờ, chúng ta hãy tạo một ứng dụng mới và kiểm tra ứng dụng cho hoạt động bên ngoài bằng cách sử dụng dự định () để hiểu khái niệm.

  • Khởi động Android studio.

  • Tạo một dự án mới như đã thảo luận trước đó và đặt tên là IntentSampleApp.

  • Di chuyển ứng dụng sang khung AndroidX bằng Refactor → Di chuyển sang menu tùy chọn AndroidX .

  • Tạo một hộp văn bản, một nút để mở danh sách liên hệ và một hộp khác để quay cuộc gọi bằng cách thay đổi activity_main.xml như được hiển thị bên dưới,

<?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>
  • Ngoài ra, hãy thêm mục bên dưới vào tệp tài nguyên string.xml ,

<string name = "phone_number">Phone number</string>
<string name = "call">Call</string>
<string name = "call_contact">Select from contact list</string>
  • Bây giờ, hãy thêm đoạn mã dưới đây vào hoạt động chính ( MainActivity.java ) theo phương thức onCreate .

public 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.setOnClickListener(new View.OnClickListener() {
         @Override
         public void onClick(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.setOnClickListener(new View.OnClickListener() {
         @Override
         public void onClick(View view) {
            if(phoneNumberEditView.getText() != null) {
               Uri number = Uri.parse("tel:" + phoneNumberEditView.getText());
               Intent callIntent = new Intent(Intent.ACTION_DIAL, number);
               startActivity(callIntent);
            }
         }
      });
   }
   // ... code
}

Ở đây, chúng ta đã lập trình nút có id, nút call_contact_button để mở danh sách liên hệ và nút có id, nút để quay cuộc gọi.

  • Thêm một biến tĩnh REQUEST_CODE trong lớp MainActivity như hình dưới đây,

public class MainActivity extends AppCompatActivity {
   // ...
   private static final int REQUEST_CODE = 1;
   // ...
}
  • Bây giờ, thêm phương thức onActivityResult trong lớp MainActivity như bên dưới,

public 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);
         }
      }
   };
   // ...
}

Tại đây, onActivityResult sẽ được gọi khi người dùng quay lại ứng dụng sau khi mở danh sách liên hệ bằng nút call_contact_button và chọn một liên hệ. Khi phương thức onActivityResult được gọi, nó sẽ nhận được người dùng đã chọn liên hệ, tìm số liên hệ và đặt nó vào hộp văn bản.

  • Chạy ứng dụng và đảm bảo mọi thứ đều ổn. Giao diện cuối cùng của Ứng dụng mẫu Intent như hình dưới đây,

  • Bây giờ, hãy cấu hình ý định espresso trong tệp gradle của ứng dụng như được hiển thị bên dưới,

dependencies {
   // ...
   androidTestImplementation 'androidx.test.espresso:espresso-intents:3.1.1'
}
  • Nhấp vào tùy chọn menu Đồng bộ hóa ngay do Android Studio cung cấp. Thao tác này sẽ tải xuống thư viện kiểm tra ý định và định cấu hình nó đúng cách.

  • Mở tệp ExampleI Kinh nguyệtTest.java và thêm IntentsTestRule thay vì AndroidTestRule thường được sử dụng . IntentTestRule là một quy tắc đặc biệt để xử lý thử nghiệm ý định.

public class ExampleInstrumentedTest {
   // ... code
   @Rule
   public IntentsTestRule<MainActivity> mActivityRule =
   new IntentsTestRule<>(MainActivity.class);
   // ... code
}
  • Thêm hai biến cục bộ để đặt số điện thoại thử nghiệm và tên gói trình quay số như bên dưới,

public 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
}
  • Khắc phục sự cố nhập bằng cách sử dụng tùy chọn Alt + Enter do android studio cung cấp hoặc nếu không, hãy bao gồm các câu lệnh nhập bên dưới,

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.click;
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.*;
  • Thêm trường hợp kiểm tra bên dưới để kiểm tra xem trình quay số có được gọi đúng hay không,

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

Ở đây, các trình so khớp hasAction , hasDatatoPackage được sử dụng cùng với trình so khớp allOf để thành công chỉ khi tất cả các trình so khớp được thông qua.

  • Bây giờ, hãy chạy Thử nghiệm ví dụ thông qua menu nội dung trong Android studio.

dự định ()

Espresso cung cấp một phương thức đặc biệt - ý định () để chế nhạo một hành động có ý định bên ngoài. ý định () chấp nhận tên gói của ý định bị chế nhạo và cung cấp phương thức phản hồiVới việc đặt cách phản hồi ý định bị chế nhạo như được chỉ định bên dưới,

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

Ở đây, responseWith () chấp nhận kết quả ý định của kiểu Instrumentation.ActivityResult . Chúng tôi có thể tạo mục đích sơ khai mới và đặt kết quả theo cách thủ công như được chỉ định bên dưới,

// 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);

Mã hoàn chỉnh để kiểm tra xem ứng dụng liên hệ có được mở đúng cách hay không như sau:

@Test
public 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 click action
   onView(withId(R.id.call_contact_button)).perform(click());
   
   // 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)));
}

Ở đây, chúng tôi đã tạo một ý định mới và đặt giá trị trả về (khi gọi ý định) làm mục nhập đầu tiên của danh sách liên hệ, nội dung: //com.android.contacts/data/1 . Sau đó, chúng tôi đã thiết lập phương thức dự định để giả lập ý định mới được tạo thay cho danh sách liên hệ. Nó đặt và gọi ý định mới được tạo của chúng tôi khi gói, com.android.contacts được gọi và mục nhập đầu tiên mặc định của danh sách được trả về. Sau đó, chúng tôi kích hoạt hành động click () để bắt đầu mục đích giả và cuối cùng kiểm tra xem số điện thoại từ việc gọi ra mục đích giả và số của mục nhập đầu tiên trong danh sách liên hệ có giống nhau hay không.

Có bất kỳ vấn đề nhập nào bị thiếu, sau đó khắc phục các vấn đề nhập đó bằng cách sử dụng tùy chọn Alt + Enter do android studio cung cấp hoặc nếu không, hãy bao gồm các câu lệnh nhập bên dưới,

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.click;
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.*;

Thêm quy tắc dưới đây trong lớp thử nghiệm để cung cấp quyền đọc danh sách liên hệ -

@Rule
public GrantPermissionRule permissionRule =
GrantPermissionRule.grant(Manifest.permission.READ_CONTACTS);

Thêm tùy chọn bên dưới vào tệp kê khai ứng dụng, AndroidManifest.xml -

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

Bây giờ, hãy đảm bảo danh sách liên hệ có ít nhất một mục nhập và sau đó chạy thử nghiệm bằng menu ngữ cảnh của Android Studio.

Android hỗ trợ kiểm tra giao diện người dùng liên quan đến nhiều ứng dụng. Hãy để chúng tôi xem xét ứng dụng của chúng tôi có một tùy chọn để chuyển từ ứng dụng của chúng tôi sang ứng dụng nhắn tin để gửi tin nhắn và sau đó quay lại ứng dụng của chúng tôi. Trong trường hợp này, khung kiểm tra trình tự động giao diện người dùng giúp chúng tôi kiểm tra ứng dụng. UI tự động có thể được coi là một người bạn đồng hành tốt cho khung thử nghiệm espresso. Chúng tôi có thể khai thác tùy chọn ý định () trong khuôn khổ thử nghiệm cà phê espresso trước khi chọn trình tự động hóa giao diện người dùng .

Hướng dẫn thiết lập

Android cung cấp trình tự động giao diện người dùng dưới dạng một plugin riêng biệt. Nó cần được định cấu hình trong app / build.gradle như được chỉ định bên dưới,

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

Quy trình làm việc cho Viết bài kiểm tra

Hãy để chúng tôi hiểu cách viết một trường hợp thử nghiệm dựa trên UI Automator ,

  • Lấy đối tượng UiDevice bằng cách gọi phương thức getInstance () và truyền đối tượng Instrumentation .

myDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
myDevice.pressHome();
  • Nhận đối tượng UiObject bằng phương thức findObject () . Trước khi sử dụng phương pháp này, chúng tôi có thể mở ứng dụng uiautomatorviewer để kiểm tra các thành phần giao diện người dùng của ứng dụng đích vì hiểu ứng dụng đích cho phép chúng tôi viết các trường hợp thử nghiệm tốt hơn.

UiObject button = myDevice.findObject(new UiSelector()
   .text("Run")
   .className("android.widget.Button"));
  • Mô phỏng tương tác của người dùng bằng cách gọi phương thức của UiObject . Ví dụ: setText () để chỉnh sửa một trường văn bản và nhấp vào () để kích hoạt sự kiện nhấp của một nút.

if(button.exists() && button.isEnabled()) {
   button.click();
}
  • Cuối cùng, chúng tôi kiểm tra xem giao diện người dùng có phản ánh trạng thái mong đợi hay không.

Viết trường hợp thử nghiệm là một công việc tẻ nhạt. Mặc dù espresso cung cấp API rất dễ dàng và linh hoạt, việc viết các trường hợp thử nghiệm có thể là một công việc lười biếng và tốn thời gian. Để khắc phục điều này, Android studio cung cấp tính năng ghi và tạo các trường hợp thử espresso. Ghi lại Thử nghiệm Espresso có sẵn trong menu Chạy .

Hãy để chúng tôi ghi lại một trường hợp thử nghiệm đơn giản trong HelloWorldApp của chúng tôi bằng cách làm theo các bước được mô tả bên dưới,

  • Mở studio Android, sau đó là ứng dụng HelloWorldApp .

  • Nhấp vào ChạyGhi thử Espresso và chọn MainActivity .

  • Ảnh chụp màn hình Máy ghi như sau,

  • Nhấp vào Thêm xác nhận . Nó sẽ mở ra màn hình ứng dụng như hình dưới đây,

  • Nhấp vào Hello World! . Các ghi màn hình để Chọn xem văn bản là như sau,

  • Một lần nữa nhấp vào Lưu xác nhận Thao tác này sẽ lưu xác nhận và hiển thị nó như sau,

  • Bấm OK . Nó sẽ mở ra một cửa sổ mới và hỏi tên của test case. Tên mặc định là MainActivityTest

  • Thay đổi tên trường hợp thử nghiệm, nếu cần.

  • Một lần nữa, nhấp vào OK . Thao tác này sẽ tạo một tệp, MainActivityTest với trường hợp thử nghiệm đã ghi của chúng tôi. Mã hóa hoàn chỉnh như sau,

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.TypeSafeMatcher;
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)
public class MainActivityTest {
   @Rule
   public ActivityTestRule<MainActivity> mActivityTestRule = new ActivityTestRule<>(MainActivity.class);
   @Test
   public 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 TypeSafeMatcher<View>() {
         @Override
         public void describeTo(Description description) {
            description.appendText("Child at position " + position + " in parent ");
            parentMatcher.describeTo(description);
         }
         @Override
         public boolean matchesSafely(View view) {
            ViewParent parent = view.getParent();
            return parent instanceof ViewGroup &&
               parentMatcher.matches(parent)&& view.equals(((ViewGroup)
               parent).getChildAt(position));
         }
      };
   }
}
  • Cuối cùng, chạy thử nghiệm bằng menu ngữ cảnh và kiểm tra xem trường hợp thử nghiệm có chạy hay không.

Trải nghiệm người dùng tích cực đóng một vai trò rất quan trọng trong sự thành công của một ứng dụng. Trải nghiệm người dùng không chỉ liên quan đến giao diện người dùng đẹp mà còn là tốc độ hiển thị của những giao diện người dùng đẹp đó và tốc độ khung hình trên giây là bao nhiêu. Giao diện người dùng cần chạy nhất quán ở tốc độ 60 khung hình / giây để mang lại trải nghiệm tốt cho người dùng.

Hãy để chúng tôi tìm hiểu một số tùy chọn có sẵn trong Android để phân tích hiệu suất giao diện người dùng trong chương này.

bãi rác

dumpsys là một công cụ tích hợp sẵn trong thiết bị Android. Nó xuất ra thông tin hiện tại về các dịch vụ hệ thống. bãi rác có tùy chọn để kết xuất thông tin về danh mục cụ thể. Chuyển gfxinfo sẽ cung cấp thông tin hoạt ảnh của gói được cung cấp. Lệnh như sau,

> adb shell dumpsys gfxinfo <PACKAGE_NAME>

framestats

framestats là một tùy chọn của lệnh kết xuất. Khi kết xuất được gọi với framestats , nó sẽ kết xuất thông tin thời gian khung chi tiết của các khung gần đây. Lệnh như sau,

> adb shell dumpsys gfxinfo <PACKAGE_NAME> framestats

Nó xuất ra thông tin dưới dạng CSV (các giá trị được phân tách bằng dấu phẩy). Đầu ra ở định dạng CSV giúp dễ dàng đẩy dữ liệu sang excel và sau đó trích xuất thông tin hữu ích thông qua các công thức và biểu đồ excel.

systrace

systrace cũng là một công cụ tích hợp sẵn trong thiết bị Android. Nó nắm bắt và hiển thị thời gian thực thi của các quy trình ứng dụng. systrace có thể được chạy bằng lệnh dưới đây trong thiết bị đầu cuối của studio android,

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

Tính năng trợ năng là một trong những tính năng chính cho bất kỳ ứng dụng nào. Ứng dụng do nhà cung cấp phát triển phải hỗ trợ nguyên tắc trợ năng tối thiểu do Android SDK đặt ra để trở thành một ứng dụng thành công và hữu ích. Tuân theo tiêu chuẩn khả năng tiếp cận là rất quan trọng và nó không phải là một nhiệm vụ dễ dàng. Android SDK cung cấp hỗ trợ tuyệt vời bằng cách cung cấp các chế độ xem được thiết kế phù hợp để tạo giao diện người dùng có thể truy cập được.

Tương tự như vậy, khung kiểm tra Espresso có lợi rất nhiều cho cả nhà phát triển và người dùng cuối bằng cách hỗ trợ một cách minh bạch các tính năng kiểm tra khả năng tiếp cận vào công cụ kiểm tra lõi.

Trong Espresso, nhà phát triển có thể kích hoạt và định cấu hình kiểm tra khả năng truy cập thông qua lớp AccessibilityChecks . Mã mẫu như sau,

AccessibilityChecks.enable();

Theo mặc định, kiểm tra khả năng tiếp cận sẽ chạy khi bạn thực hiện bất kỳ hành động xem nào. Kiểm tra bao gồm chế độ xem mà hành động được thực hiện cũng như tất cả các chế độ xem con cháu. Bạn có thể kiểm tra toàn bộ hệ thống phân cấp chế độ xem của màn hình bằng đoạn mã sau:

AccessibilityChecks.enable().setRunChecksFromRootView(true);

Phần kết luận

Espresso là một công cụ tuyệt vời dành cho các nhà phát triển Android để kiểm tra ứng dụng của họ một cách hoàn toàn dễ dàng và không cần nỗ lực thêm theo yêu cầu của một khung thử nghiệm. Nó thậm chí còn có bộ ghi để tạo trường hợp thử nghiệm mà không cần viết mã theo cách thủ công. Ngoài ra, nó hỗ trợ tất cả các loại kiểm tra giao diện người dùng. Bằng cách sử dụng khung thử nghiệm cà phê espresso, một nhà phát triển Android có thể tự tin phát triển một ứng dụng đẹp mắt cũng như một ứng dụng thành công mà không gặp bất kỳ vấn đề nào trong một khoảng thời gian ngắn.