مكتبة اختبار إجراءات التطبيق

توفّر مكتبة اختبارات "مهامّ في التطبيقات" (AATL) إمكانات تمكِّن المطوّرين لاختبار تنفيذ "إجراءات التطبيق" آليًا، وإجراء اختبارات مبرمَجة عادةً باستخدام طلبات البحث الصوتية الفعلية أو أداة اختبار مهام التطبيق.

تساعد المكتبة في التأكّد من صحة إعدادات shortcut.xml و نجاح استدعاء نية Android الموضّح. توفّر "مكتبة اختبار إجراءات التطبيقات" آلية لاختبار قدرة تطبيقك على تنفيذ نية ومعايير "مساعد Google" المحدّدة، وذلك من خلال تحويلها إلى رابط لموضع معيّن في تطبيق Android أو نية Android، والتي يمكن تأكيدها واستخدامها لإنشاء مثيل لنشاط Android.

يتم إجراء الاختبار على شكل وحدات Robolectric أو اختبارات مزوّدة بأدوات في بيئة Android. وهذا يتيح للمطوّرين إجراء اختبارات شاملة التطبيق من خلال محاكاة السلوك الفعلي للتطبيق. لاختبار عناوين URL لصفحات في التطبيق أو طلبات ملف شخصي مخصّصة أو عمليات إكمال روابط صفحات في التطبيق، يمكن استخدام أي إطار عمل اختبار مزوّد بأدوات اختبار (UI Automator أو Espresso أو JUnit4 أو Appium أو Detox أو Calabash).

وإذا كان التطبيق متعدد اللغات، فيمكن للمطورين التحقق من أن التطبيق تعمل وظيفة التطبيق بشكل صحيح في اللغات المختلفة.

آلية العمل

لدمج مكتبة اختبار مهام التطبيق في بيئة اختبار التطبيق، على المطوّرين إنشاء اختبارات جديدة أو تعديل اختبارات Robolectric أو المسجَّلة حاليًا على app التطبيق.

يحتوي رمز الاختبار على الأجزاء التالية:

  • بدء تشغيل مثيل المكتبة، في طريقة الإعداد الشائعة أو في حالات الاختبار الفردية
  • يستدعي كل اختبار فردي طريقة fulfill لمثيل المكتبة من أجل إنتاج نتيجة إنشاء النية.
  • بعد ذلك، يُثبت المطوِّر صحة الرابط لصفحة في التطبيق أو يشغّل عملية إكمال التطبيق، ويُجري عملية تحقّق مخصّصة لحالة التطبيق.

متطلّبات الإعداد

من أجل استخدام مكتبة الاختبار، يجب إجراء بعض الإعدادات الأولية للتطبيق مطلوبة قبل إضافة الاختبارات إلى طلبك.

الإعدادات

لاستخدام "مكتبة اختبار إجراءات التطبيقات"، تأكَّد من ضبط إعدادات تطبيقك على النحو التالي:

  • تثبيت المكوّن الإضافي لنظام Gradle المتوافق مع Android (AGP)
  • أدرِج ملف shortcuts.xml في مجلد res/xml في وحدة app.
  • تأكَّد من أنّ AndroidManifest.xml يتضمّن <meta-data android:name="android.app.shortcuts" android:resource=”@xml/shortcuts” /> ضمن أيّ مما يلي:
    • العلامة <application>
    • علامة مشغّل التطبيقات <activity>
  • عليك وضع العنصر <capability> داخل العنصر <shortcuts> في shortcuts.xml

إضافة تبعيات مكتبة اختبار "مهامّ في التطبيقات"

  1. إضافة مستودع Google إلى قائمة مستودعات المشاريع في settings.gradle:

        allprojects {
            repositories {
                
                google()
            }
        }
    
  2. في ملف build.gradle لوحدة التطبيق، أضِف تبعيات AATL:

        androidTestImplementation 'com.google.assistant.appactions:testing:1.0.0'
    

    تأكد من استخدام رقم إصدار المكتبة التي نزَّلتها.

إنشاء اختبارات الدمج

  1. أنشئ اختبارات جديدة ضمن 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…")))
        
  2. يجب أن تتطابق السمات الرئيسية والاسم في عمليات ربط المَعلمات مع المَعلمات من ملف BII. على سبيل المثال، يتطابق exercisePlan.forExercise.name مع مستندات المَعلمة في GET_EXERCISE_PLAN.

  3. أنشئ مثيلًا لواجهة برمجة التطبيقات باستخدام مَعلمة سياق Android (يتم الحصول عليها من ApplicationProvider أو InstrumentationRegistry):

    • بنية التطبيق ذات الوحدة الواحدة:

    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));
          }
        
      
  4. نفِّذ طريقة fulfill لواجهة برمجة التطبيقات واحصل على كائن AppActionsFulfillmentResult.

تنفيذ الأحكام

في ما يلي الطريقة المقترَحة لتأكيد "مكتبة اختبارات إجراءات التطبيقات":

  1. أكِّد نوع تنفيذ AppActionsFulfillmentResult. يجب أن أن تكون FulfillmentType.INTENT، أو FulfillmentType.UNFULFILLED لكي اختبار طريقة عمل التطبيق في حال حدوث طلبات غير متوقعة لـ BII.
  2. هناك نوعان من الطعام: INTENT وDEEPLINK.
    • في العادة، يمكن للمطوّر التفريق بين عمليات تنفيذ INTENT و DEEPLINK من خلال الاطّلاع على علامة الغرض في shortcuts.xml التي يتم تنفيذها من خلال تنشيط المكتبة.
    • إذا كانت هناك علامة نموذج عنوان URL ضمن علامة الهدف، يعني ذلك أنّ DEEPLINK يلبّي هذا الهدف.
    • إذا كانت طريقة getData() لهدف النتيجة تعرِض عنصرًا غير صفري، يشير ذلك أيضًا إلىDEEPLINK الإنجاز. وبالمثل، إذا كانت getData تعرض null، يعني ذلك أنّها عملية توصيل INTENT.
  3. بالنسبة إلى الحالة INTENT، يمكنك تحويل النوع AppActionsFulfillmentResult إلى AppActionsIntentFulfillmentResult، ثم جلب Android Intent من خلال استدعاء الأسلوب getIntent وتنفيذ أحد الإجراءات التالية:
    • تأكيد حقول فردية لطلب Android
    • تأكيد معرّف الموارد المنتظم (URI) للنية التي يتم الوصول إليها من خلال طريقة intent.getData.getHost.
  4. بالنسبة إلى حالة DEEPLINK، حوِّل AppActionsFulfillmentResult إلى AppActionsIntentFulfillmentResult (نفس سيناريو INTENT أعلاه)، يمكنك جلب Intent Android من خلال استدعاء طريقة getIntent وتأكيد عنوان URL لرابط صفحة معيّنة في التطبيق (يتم الوصول إليه من خلال intent.getData.getHost).
  5. بالنسبة إلى كل من 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);
    
  

في ما يلي مثال على اختبار AATL تم ضبطه على اللغة الإسبانية (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");
      }
      
    

تحديد المشاكل وحلّها

إذا تعذّر اختبار الدمج بشكل غير متوقّع، يمكنك البحث عن رسائل سجلّ AATL في نافذة logcat في "استوديو Android" للحصول على رسالة مستوى التحذير أو الخطأ. يمكنك أيضًا زيادة مستوى تسجيل المحتوى لتسجيل المزيد من المحتوى من المكتبة.

القيود

في ما يلي القيود الحالية على مكتبة اختبار مهام التطبيق :

  • لا تختبر AATL ميزة "فهم اللغة الطبيعية" (NLU) أو ميزة "تحويل الكلام إلى نص" (STT).
  • لا يعمل AATL عندما تكون الاختبارات في وحدات غير التطبيق التلقائي. واحدة.
  • لا يتوافق إطار عمل AATL إلا مع الإصدار 7.0 من نظام التشغيل Android (المستوى 24 لواجهة برمجة التطبيقات) والإصدارات الأحدث.