Thư viện kiểm thử Hành động trong ứng dụng (App Actions Test Library – AATL) cung cấp các tính năng cho phép nhà phát triển kiểm thử khả năng của Hành động trong ứng dụng (App Action) theo phương thức lập trình. Đây là cách kiểm thử tự động hoá mà thông thường được triển khai qua các truy vấn thoại thực tế hoặc công cụ kiểm thử Hành động trong ứng dụng.
Thư viện này giúp đảm bảo rằng cấu hình shortcut.xml
là chính xác và lệnh gọi ý định trên Android được mô tả đã thành công. Thư viện kiểm thử Hành động trong ứng dụng cung cấp cơ chế kiểm thử khả năng thực hiện một vài tham số và ý định của Trợ lý Google của ứng dụng, bằng cách chuyển đổi các đối tượng này thành một đường liên kết sâu trên Android hoặc ý định Android. Bạn có thể xác nhận và sử dụng các đường liên kết sâu hoặc ý định này để tạo một thực thể hoạt động Android.
Hoạt động kiểm thử được thực hiện dưới dạng đơn vị Robolectric hoặc kiểm thử đo lường trong môi trường Android. Điều này cho phép nhà phát triển kiểm thử ứng dụng một cách toàn diện, nhờ quy trình mô phỏng hành vi thực tế của ứng dụng. Để kiểm thử BII, ý định tuỳ chỉnh hoặc phương thức thực hiện liên kết sâu, bạn có thể sử dụng bất kỳ khung kiểm thử đo lường nào (UI Automator, Espresso, JUnit4, Appium, Detox, Calabash).
Nếu ứng dụng có nhiều ngôn ngữ thì nhà phát triển có thể xác thực rằng chức năng của ứng dụng đang hoạt động chính xác trong nhiều ngôn ngữ.
Cách thức hoạt động
Để tích hợp Thư viện kiểm thử Hành động trong ứng dụng vào môi trường thử nghiệm của ứng dụng, nhà phát triển nên tạo mới hoặc cập nhật các chương trình kiểm thử đo lường hoặc Robolectric hiện có trên mô-đun app
của ứng dụng.
Đoạn mã kiểm thử gồm các phần sau:
- Khởi động thực thể thư viện, trong phương thức thiết lập chung hoặc trong các trường hợp kiểm thử riêng lẻ.
- Mỗi lần kiểm thử riêng lẻ sẽ gọi phương thức
fulfill
của thực thể thư viện để đưa ra kết quả tạo ý định. - Sau đó, nhà phát triển sẽ xác nhận đường liên kết sâu hoặc kích hoạt phương thức thực hiện ứng dụng và chạy tính năng xác thực tuỳ chỉnh ở trạng thái ứng dụng.
Các yêu cầu về thiết lập
Để sử dụng thư viện kiểm thử, bạn cần cung cấp một số cấu hình ứng dụng ban đầu trước khi thêm nội dung kiểm thử vào ứng dụng.
Cấu hình
Để sử dụng Thư viện kiểm thử Hành động trong ứng dụng, hãy đảm bảo ứng dụng của bạn được định cấu hình như sau:
- Cài đặt Trình bổ trợ Android cho Gradle (AGP)
- Đưa tệp
shortcuts.xml
vào thư mụcres/xml
trong mô-đunapp
. - Đảm bảo
AndroidManifest.xml
bao gồm<meta-data android:name="android.app.shortcuts" android:resource=”@xml/shortcuts” />
thuộc:- thẻ
<application>
- thẻ trình chạy
<activity>
- thẻ
- Đặt phần tử
<capability>
bên trong phần tử<shortcuts>
trongshortcuts.xml
Thêm phần phụ thuộc Thư viện kiểm thử Hành động trong ứng dụng
Thêm kho lưu trữ của Google vào danh sách kho lưu trữ dự án trong
settings.gradle
:allprojects { repositories { … google() } }
Trong tệp
build.gradle
của mô-đun ứng dụng, hãy thêm các phần phụ thuộc AATL:androidTestImplementation 'com.google.assistant.appactions:testing:1.0.0'
Hãy nhớ sử dụng số phiên bản của thư viện mà bạn đã tải xuống.
Tạo chương trình kiểm thử tích hợp
Tạo chương trình kiểm thử mới trong
app/src/androidTest
. Đối với chương trình kiểm thử Robolectric, hãy tạo trongapp/src/test
:Kotlin
import android.content.Context import android.content.Intent import android.widget.TextView import androidx.test.core.app.ApplicationProvider import androidx.test.core.app.ActivityScenario import com.google.assistant.appactions.testing.aatl.AppActionsTestManager import com.google.assistant.appactions.testing.aatl.fulfillment.AppActionsFulfillmentIntentResult import com.google.assistant.appactions.testing.aatl.fulfillment.AppActionsFulfillmentResult import com.google.assistant.appactions.testing.aatl.fulfillment.FulfillmentType import com.google.common.collect.ImmutableMap import org.junit.Assert.assertEquals import org.junit.Before import org.junit.runner.RunWith import org.junit.Test import org.robolectric.RobolectricTestRunner … @Test fun IntentTestExample() { val intentParams = mapOf("feature" to "settings") val intentName = "actions.intent.OPEN_APP_FEATURE" val result = aatl.fulfill(intentName, intentParams) assertEquals(FulfillmentType.INTENT, result.getFulfillmentType()) val intentResult = result as AppActionsFulfillmentIntentResult val intent = intentResult.intent // Developer can choose to assert different relevant properties of the returned intent, such as the action, activity, package, scheme and so on assertEquals("youtube", intent.scheme) assertEquals("settings", intent.getStringExtra("featureParam")) assertEquals("actions.intent.OPEN_APP_FEATURE", intent.action) assertEquals("com.google.android.youtube/.MainActivity", intent.component.flattenToShortString()) assertEquals("com.google.myapp", intent.package) // Developers can choose to use returned Android Intent to launch and assess the activity. Below are examples for how it will look like for Robolectric and Espresso tests. // Please note that the below part is just a possible example of how Android tests are validating Activity functionality correctness for given Android Intent. // Robolectric example: val activity = Robolectric.buildActivity(MainActivity::class.java, intentResult.intent).create().resume().get() val title: TextView = activity.findViewById(R.id.startActivityTitle) assertEquals(title?.text?.toString(), "Launching…") }
Java
import android.content.Context; import android.content.Intent; import android.widget.TextView; import androidx.test.core.app.ApplicationProvider; import androidx.test.core.app.ActivityScenario; import com.google.assistant.appactions.testing.aatl.AppActionsTestManager; import com.google.assistant.appactions.testing.aatl.fulfillment.AppActionsFulfillmentIntentResult; import com.google.assistant.appactions.testing.aatl.fulfillment.AppActionsFulfillmentResult; import com.google.assistant.appactions.testing.aatl.fulfillment.FulfillmentType; import com.google.common.collect.ImmutableMap; import org.junit.Assert.assertEquals; import org.junit.Before; import org.junit.runner.RunWith; import org.junit.Test; import org.robolectric.RobolectricTestRunner; ... @Test public void IntentTestExample() throws Exception { Map<String, String> intentParams = ImmutableMap.of("feature", "settings"); String intentName = "actions.intent.OPEN_APP_FEATURE"; AppActionsFulfillmentResult result = aatl.fulfill(intentName, intentParams); assertEquals(FulfillmentType.INTENT, result.getFulfillmentType()); AppActionsFulfillmentIntentResult intentResult = (AppActionsFulfillmentIntentResult) result; Intent intent = intentResult.getIntent(); // Developer can choose to assert different relevant properties of the returned intent, such as the action, activity, package, or scheme assertEquals("settings", intent.getStringExtra("featureParam")); assertEquals("actions.intent.OPEN_APP_FEATURE", intent.getAction()); assertEquals("com.google.android.youtube/.MainActivity", intent.getComponent().flattenToShortString()); assertEquals("com.google.myapp", intent.getPackage()); // Developers can choose to use returned Android Intent to launch and assess the activity. Below are examples for how it will look like for Robolectric and Espresso tests. // Please note that the below part is just a possible example of how Android tests are validating Activity functionality correctness for given Android Intent. // Robolectric example: MainActivity activity = Robolectric.buildActivity(MainActivity.class,intentResult.intent).create().resume().get(); TextView title: TextView = activity.findViewById(R.id.startActivityTitle) assertEquals(title?.getText()?.toString(), "Launching…") }
Nếu đang sử dụng Espresso, bạn cần điều chỉnh cách chạy Hoạt động dựa trên kết quả AATL. Dưới đây là một ví dụ về Espresso dùng phương thức
ActivityScenario
:Kotlin
ActivityScenario.launch<MainActivity>(intentResult.intent); Espresso.onView(ViewMatchers.withId(R.id.startActivityTitle)) .check(ViewAssertions.matches(ViewMatchers.withText("Launching…")))
Java
ActivityScenario.launch<MainActivity>(intentResult.intent); Espresso.onView(ViewMatchers.withId(R.id.startActivityTitle)) .check(ViewAssertions.matches(ViewMatchers.withText("Launching…")))
Có tên và các thuộc tính chính trong sơ đồ ánh xạ tham số khớp với tham số trong BII. Ví dụ:
exercisePlan.forExercise.name
khớp với tài liệu cho tham số trongGET_EXERCISE_PLAN
.Tạo thực thể API bằng tham số Context của Android (lấy từ
ApplicationProvider
hoặcInstrumentationRegistry
):- Cấu trúc ứng dụng mô-đun đơn:
Kotlin
private lateinit var aatl: AppActionsTestManager @Before fun init() { val appContext = ApplicationProvider.getApplicationContext() aatl = AppActionsTestManager(appContext) }
Java
private AppActionsTestManager aatl; @Before public void init() { Context appContext = ApplicationProvider.getApplicationContext(); aatl = new AppActionsTestManager(appContext); }
- Cấu trúc ứng dụng nhiều mô-đun:
Kotlin
private lateinit var aatl: AppActionsTestManager @Before fun init() { val appContext = ApplicationProvider.getApplicationContext() val lookupPackages = listOf("com.myapp.mainapp", "com.myapp.resources") aatl = AppActionsTestManager(appContext, lookupPackages) }
Java
private AppActionsTestManager aatl; @Before public void init() throws Exception { Context appContext = ApplicationProvider.getApplicationContext(); List<String> lookupPackages = Arrays.asList("com.myapp.mainapp","com.myapp.resources"); aatl = new AppActionsTestManager(appContext, Optional.of(lookupPackages)); }
Thực thi phương thức
fulfill
của API và lấy đối tượngAppActionsFulfillmentResult
.
Xác nhận
Bạn nên xác nhận Thư viện kiểm thử Hành động trong ứng dụng theo cách dưới đây:
- Xác nhận kiểu phương thức thực hiện của
AppActionsFulfillmentResult
. Giá trị này phải làFulfillmentType.INTENT
hoặcFulfillmentType.UNFULFILLED
để kiểm thử cách ứng dụng hoạt động trong trường hợp có các yêu cầu BII bất thường. - Có 2 phiên bản phương thức thực hiện: phương thức thực hiện
INTENT
vàDEEPLINK
.- Thường thì nhà phát triển có thể phân biệt phương thức thực hiện
INTENT
vàDEEPLINK
bằng cách xem thẻ ý định trongshortcuts.xml
mà họ đang thực hiện bằng cách kích hoạt thư viện. - Nếu có một thẻ mẫu URL trong thẻ ý định, thì đây là dấu hiệu cho biết
DEEPLINK
thực hiện ý định này. - Nếu phương thức
getData()
của ý định kết quả trả về một đối tượng khác rỗng, thì điều này cũng cho biết đó là phương thức thực hiệnDEEPLINK
. Tương tự, nếugetData
trả vềnull
, thì đó là một phương thức thực hiệnINTENT
.
- Thường thì nhà phát triển có thể phân biệt phương thức thực hiện
- Đối với trường hợp
INTENT
, hãy nhậpAppActionsFulfillmentResult
vàoAppActionsIntentFulfillmentResult
, tìm nạp Android Intent bằng cách gọi phương thứcgetIntent
rồi thực hiện một trong những thao tác sau:- Xác nhận từng trường của Android Intent.
- Xác nhận URI của một ý định được truy cập thông qua phương thức intent.getData.getHost.
- Đối với trường hợp
DEEPLINK
, hãy nhậpAppActionsFulfillmentResult
vàoAppActionsIntentFulfillmentResult
(tương tự như đối vớiINTENT
ở trên), tìm nạp Android Intent bằng cách gọi phương thứcgetIntent
và xác nhận URL liên kết sâu (truy cập thông quaintent.getData.getHost
). - Đối với cả
INTENT
vàDEEPLINK
, bạn có thể sử dụng ý định kết quả để chạy hoạt động bằng khung kiểm thử Android đã chọn.
Quốc tế hoá
Nếu Ứng dụng của bạn có nhiều ngôn ngữ, bạn có thể định cấu hình kiểm thử để chạy kiểm thử trong một ngôn ngữ cụ thể. Ngoài ra, bạn có thể trực tiếp thay đổi ngôn ngữ:
Kotlin
import android.content.res.Configuration import java.util.Locale ... val newLocale = Locale("es") val conf = context.resources.configuration conf = Configuration(conf) conf.setLocale(newLocale)
Java
Locale newLocale = new Locale("es"); Configuration conf = context.getResources().getConfiguration(); conf = new Configuration(conf); conf.setLocale(newLocale);
Dưới đây là ví dụ về một chương trình kiểm thử AATL được định cấu hình thành tiếng Tây Ban Nha (ES):
Kotlin
import com.google.common.truth.Truth.assertThat import org.junit.Assert.assertEquals import android.content.Context import android.content.res.Configuration import androidx.test.platform.app.InstrumentationRegistry import com.google.assistant.appactions.testing.aatl.AppActionsTestManager import com.google.assistant.appactions.testing.aatl.fulfillment.AppActionsFulfillmentIntentResult import com.google.assistant.appactions.testing.aatl.fulfillment.AppActionsFulfillmentResult import com.google.assistant.appactions.testing.aatl.fulfillment.FulfillmentType import com.google.common.collect.ImmutableMap import java.util.Locale import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner @RunWith(RobolectricTestRunner::class) class ShortcutForDifferentLocaleTest { @Before fun setUp() { val context = InstrumentationRegistry.getInstrumentation().getContext() // change the device locale to 'es' val newLocale = Locale("es") val conf = context.resources.configuration conf = Configuration(conf) conf.setLocale(newLocale) val localizedContext = context.createConfigurationContext(conf) } @Test fun shortcutForDifferentLocale_succeeds() { val aatl = AppActionsTestManager(localizedContext) val intentName = "actions.intent.GET_EXERCISE_PLAN" val intentParams = ImmutableMap.of("exercisePlan.forExercise.name", "Running") val result = aatl.fulfill(intentName, intentParams) assertThat(result.getFulfillmentType()).isEqualTo(FulfillmentType.INTENT) val intentResult = result as AppActionsFulfillmentIntentResult assertThat(intentResult.getIntent().getData().toString()) .isEqualTo("myexercise://browse?plan=running_weekly") } }
Java
import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; import android.content.Context; import android.content.res.Configuration; import androidx.test.platform.app.InstrumentationRegistry; import com.google.assistant.appactions.testing.aatl.AppActionsTestManager; import com.google.assistant.appactions.testing.aatl.fulfillment.AppActionsFulfillmentIntentResult; import com.google.assistant.appactions.testing.aatl.fulfillment.AppActionsFulfillmentResult; import com.google.assistant.appactions.testing.aatl.fulfillment.FulfillmentType; import com.google.common.collect.ImmutableMap; import java.util.Locale; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; @Test public void shortcutForDifferentLocale_succeeds() throws Exception { Context context = InstrumentationRegistry.getInstrumentation().getContext(); // change the device locale to 'es' Locale newLocale = new Locale("es"); Configuration conf = context.getResources().getConfiguration(); conf = new Configuration(conf); conf.setLocale(newLocale); Context localizedContext = context.createConfigurationContext(conf); AppActionsTestManager aatl = new AppActionsTestManager(localizedContext); String intentName = "actions.intent.GET_EXERCISE_PLAN"; ImmutableMap<String, String> intentParams = ImmutableMap.of("exercisePlan.forExercise.name", "Running"); AppActionsFulfillmentResult result = aatl.fulfill(intentName, intentParams); assertThat(result.getFulfillmentType()).isEqualTo(FulfillmentType.INTENT); AppActionsFulfillmentIntentResult intentResult = (AppActionsFulfillmentIntentResult) result; assertThat(intentResult.getIntent().getData().toString()) .isEqualTo("myexercise://browse?plan=running_weekly"); }
Khắc phục sự cố
Nếu chương trình kiểm thử tích hợp của bạn thất bại ngoài dự kiến, bạn có thể tìm thông điệp nhật ký AATL trong cửa sổ logcat của Android Studio để xem thông báo cấp cảnh báo hoặc lỗi. Bạn cũng có thể tăng cấp độ ghi nhật ký để thu được nhiều kết quả đầu ra hơn từ thư viện.
Hạn chế
Dưới đây là những điểm hạn chế hiện tại của Thư viện kiểm thử Hành động trong ứng dụng:
- AATL không kiểm tra các tính năng Hiểu ngôn ngữ tự nhiên (NLU) hay Chuyển lời nói thành văn bản (STT).
- AATL không hoạt động khi quy trình kiểm thử nằm trong các mô-đun không phải là mô-đun ứng dụng mặc định.
- AATL chỉ tương thích với Android 7.0 "Nougat" (API cấp 24) trở lên.