Espresso-testing-intents

提供:Dev Guides
移動先:案内検索

エスプレッソテストフレームワーク-インテント

Android Intentは、内部(製品リスト画面から製品詳細画面を開く)または外部(ダイヤラーを開いて電話をかけるなど)の新しいアクティビティを開くために使用されます。 内部インテントアクティビティは、エスプレッソテストフレームワークによって透過的に処理され、ユーザー側からの特定の作業は必要ありません。 ただし、外部アクティビティを呼び出すことは、テスト対象のアプリケーションであるスコープから外れるため、本当に困難です。 ユーザーが外部アプリケーションを呼び出してテスト対象のアプリケーションを終了すると、ユーザーが定義済みのアクションシーケンスでアプリケーションに戻る可能性はかなり低くなります。 したがって、アプリケーションをテストする前にユーザーアクションを想定する必要があります。 Espressoには、この状況を処理するための2つのオプションがあります。 それらは次のとおりです。

意図されました

これにより、ユーザーはテスト対象のアプリケーションから正しいインテントが開かれていることを確認できます。

意図する

これにより、ユーザーは、カメラから写真を撮る、連絡先リストから番号をダイヤルするなどの外部アクティビティをモックし、定義済みの値セット(実際の画像の代わりにカメラから定義済みの画像など)でアプリケーションに戻ることができます。

セットアップ

Espressoはプラグインライブラリを介してインテントオプションをサポートしており、ライブラリはアプリケーションのgradleファイルで設定する必要があります。 設定オプションは次のとおりです。

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

意図されました()

Espressoインテントプラグインは、呼び出されたインテントが予期されたインテントであるかどうかを確認する特別なマッチャーを提供します。 提供されるマッチャーとマッチャーの目的は次のとおりです。

hasAction

これはインテントアクションを受け入れ、指定されたインテントに一致するマッチャーを返します。

hasData

これはデータを受け入れ、マッチャーを返します。マッチャーは呼び出し中にインテントに提供されたデータと一致します。

toPackage

これは、インテントパッケージ名を受け入れ、呼び出されたインテントのパッケージ名と一致するマッチャーを返します。

ここで、新しいアプリケーションを作成し、_intended()_を使用して外部アクティビティのアプリケーションをテストして、概念を理解しましょう。

  • Androidスタジオを起動します。
  • 前述のように新しいプロジェクトを作成し、IntentSampleAppという名前を付けます。
  • Refactor→Migrate to _AndroidX_オプションメニューを使用して、アプリケーションをAndroidXフレームワークに移行します。
  • 以下に示すように、_activity_main.xml_を変更して、テキストボックス、連絡先リストを開くボタン、およびコールをダイヤルするボタンを作成します。
<?xml version = "1.0" encoding = "utf-8"?>
<RelativeLayout xmlns:android = "http://schemas.android.com/apk/res/android"
   xmlns:app = "http://schemas.android.com/apk/res-auto"
   xmlns:tools = "http://schemas.android.com/tools"
   android:layout_width = "match_parent"
   android:layout_height = "match_parent"
   tools:context = ".MainActivity">
   <EditText
      android:id = "@+id/edit_text_phone_number"
      android:layout_width = "wrap_content"
      android:layout_height = "wrap_content"
      android:layout_centerHorizontal = "true"
      android:text = ""
      android:autofillHints = "@string/phone_number"/>
   <Button
      android:id = "@+id/call_contact_button"
      android:layout_width = "wrap_content"
      android:layout_height = "wrap_content"
      android:layout_centerHorizontal = "true"
      android:layout_below = "@id/edit_text_phone_number"
      android:text = "@string/call_contact"/>
   <Button
      android:id = "@+id/button"
      android:layout_width = "wrap_content"
      android:layout_height = "wrap_content"
      android:layout_centerHorizontal = "true"
      android:layout_below = "@id/call_contact_button"
      android:text = "@string/call"/>
</RelativeLayout>
  • また、_strings.xml_リソースファイルに以下の項目を追加し、
<string name = "phone_number">Phone number</string>
<string name = "call">Call</string>
<string name = "call_contact">Select from contact list</string>
  • 次に、onCreate_メソッドの下のメインアクティビティ(_MainActivity.java)に以下のコードを追加します。
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
}

ここでは、連絡先リストを開くためのID _call_contact_button_のボタンと、通話をダイヤルするためのID _button_のボタンをプログラムしました。

  • 以下に示すように、_MainActivity_クラスに静的変数_REQUEST_CODE_を追加します。
public class MainActivity extends AppCompatActivity {
  //...
   private static final int REQUEST_CODE = 1;
  //...
}
  • 次に、以下のように_MainActivity_クラスに_onActivityResult_メソッドを追加します。
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);
         }
      }
   };
  //...
}

ここで、_call_contact_button_ボタンを使用して連絡先リストを開き、連絡先を選択した後、ユーザーがアプリケーションに戻ると、_onActivityResult_が呼び出されます。 _onActivityResult_メソッドが呼び出されると、ユーザーが選択した連絡先を取得し、連絡先番号を見つけてテキストボックスに設定します。

  • アプリケーションを実行し、すべてが正常であることを確認します。 _Intentサンプルアプリケーション_の最終的な外観は以下のとおりです。

サンプルアプリケーション

  • 次に、アプリケーションのgradleファイルで以下に示すようにエスプレッソインテントを構成します。
dependencies {
  //...
   androidTestImplementation 'androidx.test.espresso:espresso-intents:3.1.1'
}
  • Android Studioが提供する[今すぐ同期]メニューオプションをクリックします。 これにより、インテントテストライブラリがダウンロードされ、適切に構成されます。
  • _ExampleInstrumentedTest.java_ファイルを開き、通常使用される_AndroidTestRule_の代わりに_IntentsTestRule_を追加します。 _IntentTestRule_は、インテントテストを処理する特別なルールです。
public class ExampleInstrumentedTest {
  //... code
   @Rule
   public IntentsTestRule<MainActivity> mActivityRule =
   new IntentsTestRule<>(MainActivity.class);
  //... code
}
  • 2つのローカル変数を追加して、テスト電話番号とダイヤラーパッケージ名を次のように設定します。
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
}
  • android studioが提供するAlt + Enterオプションを使用してインポートの問題を修正するか、以下のインポートステートメントを含めます。
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.*;
  • 以下のテストケースを追加して、ダイヤラーが適切に呼び出されるかどうかをテストします。
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
}

ここでは、hasAction _、 hasData_、および_toPackage_マッチャーが_allOf_マッチャーとともに使用され、すべてのマッチャーが渡された場合にのみ成功します。

  • 次に、Androidスタジオのコンテンツメニューから_ExampleInstrumentedTest_を実行します。

意図する()

Espressoは、外部インテントアクションを模擬する特別なメソッド_intending()_を提供します。 _intending()_は、モックされるインテントのパッケージ名を受け入れ、以下に指定されるように、モックされたインテントへの応答方法を設定するメソッド_respondWith_を提供します。

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

ここで、_respondWith()_は、_Instrumentation.ActivityResult_型のインテント結果を受け入れます。 新しいスタブインテントを作成し、以下に示すように結果を手動で設定できます。

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

連絡先アプリケーションが適切に開かれているかどうかをテストする完全なコードは次のとおりです。

@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)));
}

ここでは、新しいインテントを作成し、戻り値(インテントを呼び出すとき)を連絡先リストの最初のエントリ_content://com.android.contacts/data/1_として設定しました。 次に、_intending_メソッドを設定して、連絡先リストの代わりに新しく作成されたインテントをモックします。 パッケージ_com.android.contacts_が呼び出され、リストのデフォルトの最初のエントリが返されたときに、新しく作成されたインテントを設定して呼び出します。 次に、_click()_アクションを起動してモックインテントを開始し、モックインテントを呼び出した電話番号と連絡先リストの最初のエントリの番号が同じかどうかを最終的に確認します。

欠落しているインポートの問題がある場合は、Android Studioが提供するAlt + Enterオプションを使用してインポートの問題を修正するか、以下のインポートステートメントを含めます。

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

テストクラスに以下のルールを追加して、連絡先リストの読み取り許可を提供します-

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

アプリケーションマニフェストファイル、_AndroidManifest.xml_に以下のオプションを追加します-

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

次に、連絡先リストに少なくとも1つのエントリがあることを確認してから、Android Studioのコンテキストメニューを使用してテストを実行します。