應用程式動作測試程式庫 (AATL) 提供多項功能,可讓開發人員透過程式輔助測試應用程式動作執行要求,以自動化方式處理通常會使用實際語音查詢或應用程式動作測試工具進行的測試。
此程式庫可確保 shortcut.xml
設定正確無誤,並成功叫用上述 Android 意圖。應用程式動作測試程式庫提供一項機制,可將特定 Google 助理意圖和參數轉換為 Android 深層連結或 Android 意圖,藉此測試應用程式執行這些意圖和參數的能力,並可用來斷言和將 Android 活動例項化。
測試形式為 Android 環境中的 Robolectric 單元測試或檢測設備測試。這可讓開發人員模擬實際的應用程式行為,全面測試應用程式。如要測試 BII、自訂意圖或深層連結執行要求,您可以使用任何檢測設備測試架構,例如 UI Automator、Espresso、JUnit4、Appium、Detox、Calabash。
如果應用程式支援多種語言,開發人員可以在不同的語言代碼下,驗證應用程式功能是否正常運作。
運作方式
如要在應用程式測試環境中整合應用程式動作測試程式庫,開發人員應在應用程式的 app
模組中,建立或更新現有的 Robolectric 或檢測設備測試。
測試程式碼包含下列部分:
- 在常見的設定方法或個別測試案例中,將程式庫例項初始化。
- 每項個別測試都會呼叫程式庫例項的
fulfill
方法,藉此產生意圖建立結果。 - 開發人員會接著斷言深層連結或觸發應用程式執行要求,並針對應用程式狀態執行自訂驗證。
設定需求條件
如要使用測試程式庫,您必須先調整一些應用程式設定,才能將測試新增至應用程式。
設定
如要使用應用程式動作測試程式庫,請確認應用程式採用以下設定:
- 安裝 Android Gradle 外掛程式 (AGP)
- 在
app
模組的res/xml
資料夾中加入shortcuts.xml
檔案 - 確認
AndroidManifest.xml
在下列任一項目底下包含<meta-data android:name="android.app.shortcuts" android:resource=”@xml/shortcuts” />
:<application>
標記- 啟動器
<activity>
標記
- 將
<capability>
元素放入shortcuts.xml
的<shortcuts>
元素中
新增應用程式動作測試程式庫依附元件
將 Google 存放區新增至
settings.gradle
的專案存放區清單中:allprojects { repositories { … google() } }
在應用程式模組
build.gradle
檔案中,新增 AATL 依附元件:androidTestImplementation 'com.google.assistant.appactions:testing:1.0.0'
請務必使用所下載程式庫的版本編號。
建立整合測試
在
app/src/androidTest
底下建立新測試。若是 Robolectric 測試,請在app/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…") }
如果使用 Espresso,就需要根據 AATL 結果,修改啟動活動的方式。以下是 Espresso 使用
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…")))
參數對應項目中的名稱和鍵屬性必須與 BII 中的參數相符,例如
exercisePlan.forExercise.name
須與GET_EXERCISE_PLAN
中參數的說明文件相符。使用 Android 結構定義參數 (可從
ApplicationProvider
或InstrumentationRegistry
取得),對 API 例項執行例項化程序:- 單一模組應用程式架構:
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); }
- 多模組應用程式架構:
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)); }
執行 API 的
fulfill
方法,並取得AppActionsFulfillmentResult
物件。
執行斷言
以下為斷言應用程式動作測試資料庫的建議做法:
- 斷言
AppActionsFulfillmentResult
的執行要求類型。所斷言的類型必須是FulfillmentType.INTENT
或FulfillmentType.UNFULFILLED
,才能測試應用程式在發生非預期 BII 要求時的行為。 - 執行要求分為 2 種:
INTENT
和DEEPLINK
執行要求。- 一般來說,如要辨別
INTENT
和DEEPLINK
執行要求,開發人員只需在執行要求透過觸發程式庫執行的shortcuts.xml
中查看意圖標記即可。 - 如果意圖標記下方有 url-template 標記,就表示執行意圖的是
DEEPLINK
。 - 如果結果意圖的
getData()
方法傳回非空值的物件,也表示執行要求類型為DEEPLINK
。同樣地,如果getData
傳回null
,即表示執行要求類型為INTENT
。
- 一般來說,如要辨別
- 若是
INTENT
的情況,請將AppActionsFulfillmentResult
轉換類型至AppActionsIntentFulfillmentResult
,然後透過呼叫getIntent
方法擷取 Android 意圖,並執行下列其中一項操作:- 斷言 Android 意圖的個別欄位。
- 斷言可透過 intent.getData.getHost 方法存取的意圖 URI。
- 若是
DEEPLINK
的情況,請將AppActionsFulfillmentResult
轉換類型至AppActionsIntentFulfillmentResult
(與上述INTENT
情況相同),然後透過呼叫getIntent
擷取 Android 意圖,並斷言深層連結網址 (可透過intent.getData.getHost
存取)。 - 對於
INTENT
和DEEPLINK
,都可以使用產生的意圖,針對選定的 Android 測試架構來啟動活動。
國際化
如果應用程式支援多種語言,您可以設定在特定受測試語言代碼下進行測試。您也可以直接變更語言代碼:
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);
以下範例是針對西班牙文 (ES) 語言代碼設定 AATL 測試:
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"); }
疑難排解
如果整合測試出現預期外的失敗情形,您可以在 Android Studio Logcat 視窗中尋找 AATL 記錄訊息,取得警示或錯誤層級訊息。您也可以提高記錄層級,從程式庫擷取更多輸出內容。
限制
以下是應用程式動作測試程式庫目前的限制:
- AATL 不會測試自然語言理解 (NLU) 或語音轉文字 (STT) 功能。
- 如果測試位於預設應用程式模組以外的模組中,AATL 會無法運作。
- AATL 只與 Android 7.0「Nougat」(API 級別 24) 以上版本相容。