กรอบการทดสอบเอสเปรสโซ - เจตนา
Android Intent ใช้เพื่อเปิดกิจกรรมใหม่ทั้งภายใน (การเปิดหน้าจอรายละเอียดผลิตภัณฑ์จากหน้าจอรายการผลิตภัณฑ์) หรือภายนอก (เช่นการเปิดแป้นหมุนเพื่อโทรออก) กิจกรรมความตั้งใจภายในได้รับการจัดการอย่างโปร่งใสโดยกรอบการทดสอบเอสเพรสโซและไม่จำเป็นต้องมีการทำงานที่เฉพาะเจาะจงจากฝั่งผู้ใช้ อย่างไรก็ตามการเรียกใช้กิจกรรมภายนอกถือเป็นความท้าทายอย่างแท้จริงเพราะมันอยู่นอกขอบเขตของเราซึ่งเป็นแอปพลิเคชันที่อยู่ระหว่างการทดสอบ เมื่อผู้ใช้เรียกใช้แอปพลิเคชันภายนอกและออกจากแอปพลิเคชันภายใต้การทดสอบโอกาสที่ผู้ใช้จะกลับมาที่แอปพลิเคชันโดยมีลำดับการดำเนินการที่กำหนดไว้ล่วงหน้าค่อนข้างน้อย ดังนั้นเราต้องถือว่าการกระทำของผู้ใช้ก่อนที่จะทดสอบแอปพลิเคชัน เอสเปรสโซมีสองทางเลือกในการจัดการกับสถานการณ์นี้ มีดังนี้
ตั้งใจ
สิ่งนี้ช่วยให้ผู้ใช้ตรวจสอบว่าเจตนาที่ถูกต้องถูกเปิดจากแอปพลิเคชันที่อยู่ระหว่างการทดสอบ
ตั้งใจ
สิ่งนี้ช่วยให้ผู้ใช้สามารถล้อเลียนกิจกรรมภายนอกเช่นถ่ายภาพจากกล้องหมุนหมายเลขจากรายชื่อผู้ติดต่อ ฯลฯ และกลับไปที่แอปพลิเคชันพร้อมชุดค่าที่กำหนดไว้ล่วงหน้า (เช่นภาพที่กำหนดไว้ล่วงหน้าจากกล้องแทนที่จะเป็นภาพจริง) .
ติดตั้ง
Espresso รองรับตัวเลือกความตั้งใจผ่านไลบรารีปลั๊กอินและไลบรารีต้องได้รับการกำหนดค่าในไฟล์ gradle ของแอปพลิเคชัน ตัวเลือกการกำหนดค่ามีดังนี้
dependencies {
// ...
androidTestImplementation 'androidx.test.espresso:espresso-intents:3.1.1'
}
ตั้งใจ ()
ปลั๊กอิน Espresso Intent ให้ตัวจับคู่พิเศษเพื่อตรวจสอบว่าเจตนาที่เรียกนั้นเป็นเจตนาที่คาดหวังหรือไม่ ผู้จับคู่ที่ระบุและวัตถุประสงค์ของผู้จับคู่มีดังนี้
hasAction
สิ่งนี้ยอมรับการดำเนินการตามเจตนาและส่งคืนตัวจับคู่ซึ่งตรงกับเจตนาที่ระบุ
hasData
สิ่งนี้ยอมรับข้อมูลและส่งคืนตัวจับคู่ซึ่งตรงกับข้อมูลที่ให้ไว้กับเจตนาในขณะที่เรียกใช้
toPackage
สิ่งนี้ยอมรับชื่อแพ็กเกจความตั้งใจและส่งคืนตัวจับคู่ซึ่งตรงกับชื่อแพ็กเกจของเจตนาที่เรียกใช้
ตอนนี้ให้เราสร้างแอปพลิเคชันใหม่และทดสอบแอปพลิเคชันสำหรับกิจกรรมภายนอกโดยใช้ตั้งใจ ()เพื่อทำความเข้าใจแนวคิด
เริ่ม Android studio
สร้างโครงการใหม่ตามที่กล่าวไว้ก่อนหน้านี้และตั้งชื่อว่า IntentSampleApp
ย้ายแอปพลิเคชันไปยังเฟรมเวิร์ก AndroidX โดยใช้Refactor →ย้ายไปที่เมนูตัวเลือก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>
ตอนนี้เพิ่มด้านล่างรหัสในกิจกรรมหลัก ( 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
}
ที่นี่เราได้ตั้งโปรแกรมปุ่มด้วย id, call_contact_buttonเพื่อเปิดรายชื่อผู้ติดต่อและปุ่มที่มี id ปุ่มเพื่อโทรออก
เพิ่มตัวแปรคงREQUEST_CODEในคลาสMainActivityดังที่แสดงด้านล่าง
public class MainActivity extends AppCompatActivity {
// ...
private static final int REQUEST_CODE = 1;
// ...
}
ตอนนี้เพิ่มเมธอด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แล้วจะได้รับรายชื่อติดต่อที่ผู้ใช้เลือกค้นหาหมายเลขติดต่อและตั้งค่าลงในกล่องข้อความ
เรียกใช้แอปพลิเคชันและตรวจสอบให้แน่ใจว่าทุกอย่างเรียบร้อยดี รูปลักษณ์สุดท้ายของแอปพลิเคชันตัวอย่างเจตนามีดังที่แสดงด้านล่าง
ตอนนี้กำหนดค่าเจตนาเอสเพรสโซในไฟล์ gradle ของแอปพลิเคชันดังที่แสดงด้านล่าง
dependencies {
// ...
androidTestImplementation 'androidx.test.espresso:espresso-intents:3.1.1'
}
คลิกตัวเลือกเมนูSync Now ที่ 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 หรืออื่น ๆ รวมถึงคำสั่งการนำเข้าด้านล่าง
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เพื่อให้สำเร็จก็ต่อเมื่อตัวจับคู่ทั้งหมดถูกส่งผ่าน
ตอนนี้เรียกใช้ExampleInstrumentedTestผ่านเมนูเนื้อหาใน Android studio
ตั้งใจ ()
เอสเปรสโซมีวิธีพิเศษ - ตั้งใจ ()เพื่อล้อเลียนการกระทำโดยเจตนาภายนอก ตั้งใจ ()ยอมรับชื่อแพคเกจของเจตนาที่จะล้อเลียนและให้วิธีการ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)));
}
ที่นี่เราได้สร้างความตั้งใจและการตั้งค่าตอบแทนใหม่ (เมื่ออัญเชิญเจตนา) เป็นรายการแรกของรายชื่อผู้ติดต่อของเนื้อหา: //com.android.contacts/data/1 จากนั้นเราได้ตั้งค่าวิธีการตั้งใจที่จะเยาะเย้ยเจตนาที่สร้างขึ้นใหม่แทนรายชื่อผู้ติดต่อ มันตั้งค่าและเรียกใช้ความตั้งใจที่สร้างขึ้นใหม่ของเราเมื่อมีการเรียกใช้แพ็คเกจ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