Espresso Testing Framework - Absichten
Android Intent wird verwendet, um neue Aktivitäten zu öffnen, entweder intern (Öffnen eines Produktdetailbildschirms über den Produktlistenbildschirm) oder extern (wie Öffnen eines Dialers, um einen Anruf zu tätigen). Interne Intent-Aktivitäten werden vom Espresso-Test-Framework transparent behandelt und erfordern keine spezifische Arbeit seitens des Benutzers. Das Aufrufen externer Aktivitäten ist jedoch eine echte Herausforderung, da dies außerhalb unseres Anwendungsbereichs liegt, der zu testenden Anwendung. Sobald der Benutzer eine externe Anwendung aufruft und die zu testende Anwendung verlässt, ist die Wahrscheinlichkeit geringer, dass der Benutzer mit einer vordefinierten Abfolge von Aktionen zur Anwendung zurückkehrt. Daher müssen wir die Benutzeraktion übernehmen, bevor wir die Anwendung testen. Espresso bietet zwei Optionen, um mit dieser Situation umzugehen. Sie sind wie folgt,
beabsichtigt
Auf diese Weise kann der Benutzer sicherstellen, dass die richtige Absicht in der zu testenden Anwendung geöffnet wird.
beabsichtigt
Auf diese Weise kann der Benutzer eine externe Aktivität verspotten, z. B. ein Foto von der Kamera aufnehmen, eine Nummer aus der Kontaktliste wählen usw. und mit vordefinierten Werten zur Anwendung zurückkehren (z. B. vordefiniertes Bild von der Kamera anstelle des tatsächlichen Bilds). .
Konfiguration
Espresso unterstützt die Intent-Option über eine Plugin-Bibliothek und die Bibliothek muss in der Gradle-Datei der Anwendung konfiguriert werden. Die Konfigurationsoption lautet wie folgt:
dependencies {
// ...
androidTestImplementation 'androidx.test.espresso:espresso-intents:3.1.1'
}
beabsichtigt()
Das Espresso Intent Plugin bietet spezielle Matcher, um zu überprüfen, ob die aufgerufene Absicht die erwartete Absicht ist. Die bereitgestellten Matcher und der Zweck der Matcher sind wie folgt:
hasAction
Dies akzeptiert die Absichtsaktion und gibt einen Matcher zurück, der der angegebenen Absicht entspricht.
hasData
Dadurch werden die Daten akzeptiert und ein Matcher zurückgegeben, der die beim Abrufen bereitgestellten Daten mit der Absicht übereinstimmt.
toPackage
Dies akzeptiert den Intent-Paketnamen und gibt einen Matcher zurück, der mit dem Paketnamen der aufgerufenen Intent übereinstimmt.
Lassen Sie uns nun eine neue Anwendung erstellen und die Anwendung mit beabsichtigten () auf externe Aktivitäten testen , um das Konzept zu verstehen.
Starten Sie Android Studio.
Erstellen Sie ein neues Projekt wie zuvor beschrieben und nennen Sie es IntentSampleApp.
Migrieren Sie die Anwendung auf AndroidX Framework Umgestalten → Migrate zu AndroidX Optionsmenü.
Erstellen Sie ein Textfeld, eine Schaltfläche zum Öffnen der Kontaktliste und eine weitere zum Wählen eines Anrufs, indem Sie die Datei activity_main.xml wie unten gezeigt ändern.
<?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>
Fügen Sie außerdem die folgenden Artikel in strings.xml Ressourcendatei,
<string name = "phone_number">Phone number</string>
<string name = "call">Call</string>
<string name = "call_contact">Select from contact list</string>
Fügen Sie nun den folgenden Code in die Hauptaktivität ( MainActivity.java ) unter der onCreate- Methode ein.
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
}
Hier haben wir die Schaltfläche mit id programmiert, call_contact_button die Kontaktliste zu öffnen und Schaltfläche mit id, Taste die Nummer zu wählen.
Fügen Sie eine statische Variable REQUEST_CODE in der MainActivity- Klasse hinzu, wie unten gezeigt.
public class MainActivity extends AppCompatActivity {
// ...
private static final int REQUEST_CODE = 1;
// ...
}
Fügen Sie nun die onActivityResult- Methode in der MainActivity- Klasse wie folgt hinzu :
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);
}
}
};
// ...
}
Hier wird onActivityResult aufgerufen, wenn ein Benutzer zur Anwendung zurückkehrt, nachdem er die Kontaktliste mit der Schaltfläche call_contact_button geöffnet und einen Kontakt ausgewählt hat. Sobald die onActivityResult- Methode aufgerufen wird, erhält sie den vom Benutzer ausgewählten Kontakt, sucht die Kontaktnummer und legt sie in das Textfeld ein.
Führen Sie die Anwendung aus und stellen Sie sicher, dass alles in Ordnung ist. Das endgültige Erscheinungsbild der Intent-Beispielanwendung ist wie folgt:
Konfigurieren Sie nun die Espresso-Absicht in der Gradle-Datei der Anwendung wie unten gezeigt.
dependencies {
// ...
androidTestImplementation 'androidx.test.espresso:espresso-intents:3.1.1'
}
Klicken Sie auf die Menüoption Jetzt synchronisieren, die vom Android Studio bereitgestellt wird. Dadurch wird die Absichtstestbibliothek heruntergeladen und ordnungsgemäß konfiguriert.
Öffnen Sie die Datei ExampleInstrumentedTest.java und fügen Sie die IntentsTestRule anstelle der normalerweise verwendeten AndroidTestRule hinzu . IntentTestRule ist eine spezielle Regel für das Testen von Absichten.
public class ExampleInstrumentedTest {
// ... code
@Rule
public IntentsTestRule<MainActivity> mActivityRule =
new IntentsTestRule<>(MainActivity.class);
// ... code
}
Fügen Sie zwei lokale Variablen hinzu, um die Testtelefonnummer und den Namen des Dialer-Pakets wie folgt festzulegen:
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
}
Beheben Sie die Importprobleme mithilfe der von Android Studio bereitgestellten Option Alt + Eingabetaste oder fügen Sie die folgenden Importanweisungen hinzu:
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.*;
Fügen Sie den folgenden Testfall hinzu, um zu testen, ob der Dialer ordnungsgemäß aufgerufen wird.
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
}
Hier werden die Matcher hasAction , hasData und toPackage zusammen mit allOf Matcher verwendet, um nur dann erfolgreich zu sein, wenn alle Matcher übergeben wurden.
Führen Sie jetzt den ExampleInstrumentedTest über das Inhaltsmenü in Android Studio aus.
beabsichtigen ()
Espresso bietet eine spezielle Methode - intending () , um eine externe Absichtsaktion zu verspotten. intending () akzeptiert den Paketnamen der zu verspottenden Absicht und stellt eine Methode replyWith bereit, mit der festgelegt wird, wie auf die verspottete Absicht reagiert werden soll, wie unten angegeben.
intending(toPackage("com.android.contacts")).respondWith(result);
Hier akzeptiert replyWith () das Absichtsergebnis vom Typ Instrumentation.ActivityResult . Wir können eine neue Stub-Absicht erstellen und das Ergebnis manuell wie unten angegeben festlegen.
// 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);
Der vollständige Code zum Testen, ob eine Kontaktanwendung ordnungsgemäß geöffnet wurde, lautet wie folgt:
@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)));
}
Hier haben wir eine neue Absicht erstellt und den Rückgabewert (beim Aufrufen der Absicht) als ersten Eintrag in der Kontaktliste festgelegt, Inhalt: //com.android.contacts/data/1 . Dann haben wir die beabsichtigte Methode festgelegt, um die neu erstellte Absicht anstelle der Kontaktliste zu verspotten. Es legt unsere neu erstellte Absicht fest und ruft sie auf, wenn das Paket com.android.contacts aufgerufen und der erste Standardeintrag der Liste zurückgegeben wird. Dann haben wir die Aktion click () ausgelöst, um die Scheinabsicht zu starten, und schließlich überprüft, ob die Telefonnummer beim Aufrufen der Scheinabsicht und die Nummer des ersten Eintrags in der Kontaktliste identisch sind.
Wenn ein Importproblem fehlt, beheben Sie diese Importprobleme mithilfe der von Android Studio bereitgestellten Option Alt + Eingabetaste oder fügen Sie die folgenden Importanweisungen hinzu.
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.*;
Fügen Sie der Testklasse die folgende Regel hinzu, um die Berechtigung zum Lesen der Kontaktliste zu erteilen.
@Rule
public GrantPermissionRule permissionRule =
GrantPermissionRule.grant(Manifest.permission.READ_CONTACTS);
Fügen Sie die folgende Option in die Anwendungsmanifestdatei hinzu: AndroidManifest.xml -
<uses-permission android:name = "android.permission.READ_CONTACTS" />
Stellen Sie nun sicher, dass die Kontaktliste mindestens einen Eintrag enthält, und führen Sie den Test über das Kontextmenü von Android Studio aus.