Android Intent используется для открытия нового действия, либо внутреннего (открытие экрана сведений о продукте на экране списка продуктов), либо внешнего (например, открытие номеронабирателя для совершения вызова). Внутренние намерения прозрачно обрабатываются средой тестирования эспрессо и не требуют какой-либо специальной работы со стороны пользователя. Тем не менее, вызывать внешнюю активность действительно сложно, потому что она выходит за рамки нашего приложения, тестируемого приложения. Как только пользователь вызывает внешнее приложение и выходит из тестируемого приложения, вероятность того, что пользователь вернется в приложение с заранее определенной последовательностью действий, будет значительно меньше. Поэтому нам нужно принять на себя действие пользователя перед тестированием приложения. Эспрессо предоставляет две возможности справиться с этой ситуацией. Они заключаются в следующем,
предназначена
Это позволяет пользователю убедиться, что правильное намерение открыто из тестируемого приложения.
намеревающийся
Это позволяет пользователю высмеивать внешнюю активность, например снимать фотографию с камеры, набирать номер из списка контактов и т. Д., И возвращаться к приложению с предопределенным набором значений (например, предопределенное изображение с камеры вместо фактического изображения). ,
Настроить
Espresso поддерживает опцию intent через библиотеку плагинов, и ее необходимо настроить в файле gradle приложения. Вариант конфигурации заключается в следующем,
dependencies { // ... androidTestImplementation 'androidx.test.espresso:espresso-intents:3.1.1' }
предназначена()
Плагин Espresso intent предоставляет специальные средства сопоставления, чтобы проверить, является ли вызванное намерение ожидаемым. Предоставленные сопоставители и их назначение заключаются в следующем:
hasAction
Это принимает действие намерения и возвращает совпадение, которое соответствует указанному намерению.
hasData
Это принимает данные и возвращает сопоставление, которое сопоставляет данные, предоставленные намерению, при его вызове.
toPackage
Это принимает имя пакета намерения и возвращает сопоставление, которое совпадает с именем пакета вызванного намерения.
Теперь давайте создадим новое приложение и протестируем приложение на внешнюю активность, используя предназначенный (), чтобы понять концепцию.
-
Запустите Android-студию.
-
Создайте новый проект, как обсуждалось ранее, и назовите его IntentSampleApp.
-
Перенесите приложение на платформу AndroidX, используя меню параметров Refactor → Migrate to AndroidX .
-
Создайте текстовое поле, кнопку, чтобы открыть список контактов, и еще одну, чтобы набрать звонок, изменив файл activity_main.xml, как показано ниже,
Запустите Android-студию.
Создайте новый проект, как обсуждалось ранее, и назовите его IntentSampleApp.
Перенесите приложение на платформу AndroidX, используя меню параметров Refactor → Migrate to 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 ,
Также добавьте следующий элемент в файл ресурсов strings.xml ,
<string name = "phone_number">Phone number</string> <string name = "call">Call</string> <string name = "call_contact">Select from contact list</string>
-
Теперь добавьте приведенный ниже код в основное действие ( MainActivity.java ) в методе onCreate .
Теперь добавьте приведенный ниже код в основное действие ( MainActivity.java ) в методе 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 }
Здесь мы запрограммировали кнопку с идентификатором call_contact_button, чтобы открыть список контактов, и кнопку с идентификатором, кнопку, чтобы набрать звонок.
-
Добавьте статическую переменную REQUEST_CODE в класс MainActivity, как показано ниже,
Добавьте статическую переменную REQUEST_CODE в класс MainActivity, как показано ниже,
public class MainActivity extends AppCompatActivity { // ... private static final int REQUEST_CODE = 1; // ... }
-
Теперь добавьте метод onActivityResult в класс MainActivity, как показано ниже:
Теперь добавьте метод onActivityResult в класс MainActivity, как показано ниже:
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); } } }; // ... }
Здесь onActivityResult будет вызываться, когда пользователь возвращается в приложение после открытия списка контактов с помощью кнопки call_contact_button и выбора контакта. Как только метод onActivityResult вызывается, он получает выбранный пользователем контакт, находит номер контакта и устанавливает его в текстовое поле.
-
Запустите приложение и убедитесь, что все в порядке. Окончательный вид заявки Intent приведен ниже,
Запустите приложение и убедитесь, что все в порядке. Окончательный вид заявки Intent приведен ниже,
-
Теперь настройте намерение эспрессо в файле gradle приложения, как показано ниже,
Теперь настройте намерение эспрессо в файле gradle приложения, как показано ниже,
dependencies { // ... androidTestImplementation 'androidx.test.espresso:espresso-intents:3.1.1' }
-
Выберите пункт меню « Синхронизировать сейчас», предоставляемый Android Studio. Это загрузит библиотеку тестирования намерений и настроит ее должным образом.
-
Откройте файл ExampleInstrumentedTest.java и добавьте IntentsTestRule вместо обычно используемого AndroidTestRule . IntentTestRule — это специальное правило для обработки намеренного тестирования.
Выберите пункт меню « Синхронизировать сейчас», предоставляемый Android Studio. Это загрузит библиотеку тестирования намерений и настроит ее должным образом.
Откройте файл ExampleInstrumentedTest.java и добавьте IntentsTestRule вместо обычно используемого AndroidTestRule . IntentTestRule — это специальное правило для обработки намеренного тестирования.
public class ExampleInstrumentedTest { // ... code @Rule public IntentsTestRule<MainActivity> mActivityRule = new IntentsTestRule<>(MainActivity.class); // ... code }
-
Добавьте две локальные переменные, чтобы установить тестовый номер телефона и имя пакета номеронабирателя, как показано ниже,
Добавьте две локальные переменные, чтобы установить тестовый номер телефона и имя пакета номеронабирателя, как показано ниже,
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 }
-
Исправьте проблемы с импортом, используя опцию Alt + Enter, предоставляемую android studio, или включите нижеприведенные операторы импорта,
Исправьте проблемы с импортом, используя опцию Alt + Enter, предоставляемую android studio, или включите нижеприведенные операторы импорта,
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 matcher для достижения успеха, только если все сопоставления пройдены.
-
Теперь запустите ExampleInstrumentedTest через меню контента в Android-студии.
Теперь запустите ExampleInstrumentedTest через меню контента в Android-студии.
намереваясь ()
Эспрессо предоставляет специальный метод — намерение (), чтобы высмеивать действие внешнего намерения. intending () принимает имя пакета намерения, которое должно быть смоделировано, и предоставляет метод responseWith, чтобы установить, как смоделированное намерение должно отвечать, как указано ниже,
intending(toPackage("com.android.contacts")).respondWith(result);
Здесь responseWith () принимает намеренный результат типа 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))); }
Здесь мы создали новое намерение и установили возвращаемое значение (при вызове намерения) в качестве первой записи списка контактов, содержимое: //com.android.contacts/data/1 . Затем мы установили метод intending, чтобы смоделировать вновь созданное намерение вместо списка контактов. Он устанавливает и вызывает наше вновь созданное намерение, когда вызывается пакет com.android.contacts и возвращается первая запись списка по умолчанию. Затем мы запустили действие click (), чтобы запустить фиктивное намерение и, наконец, проверили, совпадают ли телефонный номер из вызывающего фиктивного намерения и номер первой записи в списке контактов.
Если есть проблема с отсутствующим импортом, исправьте эти проблемы с помощью опции Alt + Enter, предоставляемой android studio, или включите приведенные ниже операторы импорта,
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" />
Теперь убедитесь, что в списке контактов есть хотя бы одна запись, а затем запустите тест с помощью контекстного меню Android Studio.