Khung thử nghiệm Espresso - Ý định
Android Intent được sử dụng để mở hoạt động mới, nội bộ (mở màn hình chi tiết sản phẩm từ màn hình danh sách sản phẩm) hoặc bên ngoài (như mở trình quay số để thực hiện cuộc gọi). Hoạt động có mục đích nội bộ được xử lý minh bạch bởi khuôn khổ thử nghiệm cà phê espresso và nó không cần bất kỳ công việc cụ thể nào từ phía người dùng. Tuy nhiên, việc gọi hoạt động bên ngoài thực sự là một thách thức vì nó vượt ra khỏi phạm vi của chúng tôi, ứng dụng đang được thử nghiệm. Khi người dùng gọi một ứng dụng bên ngoài và thoát ra khỏi ứng dụng đang được kiểm tra, thì cơ hội người dùng quay lại ứng dụng với chuỗi hành động được xác định trước sẽ ít hơn. Do đó, chúng tôi cần giả định hành động của người dùng trước khi thử nghiệm ứng dụng. Espresso cung cấp hai tùy chọn để xử lý tình huống này. Chúng như sau,
dự định
Điều này cho phép người dùng đảm bảo rằng mục đích chính xác được mở từ ứng dụng đang được thử nghiệm.
dự định
Điều này cho phép người dùng mô phỏng một hoạt động bên ngoài như chụp ảnh từ máy ảnh, quay số từ danh sách liên hệ, v.v. và quay lại ứng dụng với bộ giá trị được xác định trước (như hình ảnh xác định trước từ máy ảnh thay vì hình ảnh thực tế) .
Thiết lập
Espresso hỗ trợ tùy chọn ý định thông qua thư viện plugin và thư viện cần được định cấu hình trong tệp gradle của ứng dụng. Tùy chọn cấu hình như sau,
dependencies {
// ...
androidTestImplementation 'androidx.test.espresso:espresso-intents:3.1.1'
}
dự định ()
Plugin mục đích Espresso cung cấp các đối sánh đặc biệt để kiểm tra xem mục đích được gọi có phải là mục đích dự kiến hay không. Các trình kết hợp được cung cấp và mục đích của các trình kết hợp như sau,
hasAction
Điều này chấp nhận hành động ý định và trả về một đối sánh phù hợp với ý định đã chỉ định.
hasData
Điều này chấp nhận dữ liệu và trả về một đối sánh, khớp với dữ liệu được cung cấp cho ý định trong khi gọi nó.
đóng gói
Điều này chấp nhận tên gói ý định và trả về một đối sánh, khớp với tên gói của ý định được gọi.
Bây giờ, chúng ta hãy tạo một ứng dụng mới và kiểm tra ứng dụng cho hoạt động bên ngoài bằng cách sử dụng dự định () để hiểu khái niệm.
Khởi động Android studio.
Tạo một dự án mới như đã thảo luận trước đó và đặt tên là IntentSampleApp.
Di chuyển ứng dụng sang khung AndroidX bằng Trình tái cấu trúc → Di chuyển sang menu tùy chọn AndroidX .
Tạo một hộp văn bản, một nút để mở danh sách liên hệ và một hộp khác để quay cuộc gọi bằng cách thay đổi activity_main.xml như được hiển thị bên dưới,
<?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>
Ngoài ra, hãy thêm mục bên dưới vào tệp tài nguyên string.xml ,
<string name = "phone_number">Phone number</string>
<string name = "call">Call</string>
<string name = "call_contact">Select from contact list</string>
Bây giờ, hãy thêm đoạn mã dưới đây vào hoạt động chính ( MainActivity.java ) theo phương thức 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
}
Ở đây, chúng ta đã lập trình nút có id, nút call_contact_button để mở danh sách liên hệ và nút có id, nút để quay cuộc gọi.
Thêm một biến tĩnh REQUEST_CODE trong lớp MainActivity như hình dưới đây,
public class MainActivity extends AppCompatActivity {
// ...
private static final int REQUEST_CODE = 1;
// ...
}
Bây giờ, thêm phương thức onActivityResult trong lớp MainActivity như bên dưới,
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);
}
}
};
// ...
}
Tại đây, onActivityResult sẽ được gọi khi người dùng quay lại ứng dụng sau khi mở danh sách liên hệ bằng nút call_contact_button và chọn một liên hệ. Khi phương thức onActivityResult được gọi, nó sẽ được người dùng chọn liên hệ, tìm số liên hệ và đặt nó vào hộp văn bản.
Chạy ứng dụng và đảm bảo rằng mọi thứ đều ổn. Giao diện cuối cùng của Ứng dụng mẫu Intent như hình dưới đây,
Bây giờ, hãy cấu hình ý định espresso trong tệp gradle của ứng dụng như được hiển thị bên dưới,
dependencies {
// ...
androidTestImplementation 'androidx.test.espresso:espresso-intents:3.1.1'
}
Nhấp vào tùy chọn menu Đồng bộ hóa ngay do Android Studio cung cấp. Thao tác này sẽ tải xuống thư viện kiểm tra ý định và định cấu hình nó đúng cách.
Mở tệp ExampleI Kinh nguyệtTest.java và thêm IntentsTestRule thay vì AndroidTestRule thường được sử dụng . IntentTestRule là một quy tắc đặc biệt để xử lý thử nghiệm ý định.
public class ExampleInstrumentedTest {
// ... code
@Rule
public IntentsTestRule<MainActivity> mActivityRule =
new IntentsTestRule<>(MainActivity.class);
// ... code
}
Thêm hai biến cục bộ để đặt số điện thoại thử nghiệm và tên gói trình quay số như bên dưới,
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
}
Khắc phục sự cố nhập bằng cách sử dụng tùy chọn Alt + Enter do android studio cung cấp hoặc nếu không, hãy bao gồm các câu lệnh nhập bên dưới,
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.*;
Thêm trường hợp kiểm tra bên dưới để kiểm tra xem trình quay số có được gọi đúng hay không,
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
}
Ở đây, các trình so khớp hasAction , hasData và toPackage được sử dụng cùng với trình so khớp allOf để thành công chỉ khi tất cả các trình so khớp được thông qua.
Bây giờ, hãy chạy Thử nghiệm ví dụ thông qua menu nội dung trong Android studio.
dự định ()
Espresso cung cấp một phương thức đặc biệt - có ý định () để chế nhạo một hành động có ý định bên ngoài. ý định () chấp nhận tên gói của ý định bị chế nhạo và cung cấp một phương thức phản hồiVới việc đặt cách ý định bị chế nhạo cần được phản hồi như được chỉ định bên dưới,
intending(toPackage("com.android.contacts")).respondWith(result);
Ở đây, responseWith () chấp nhận kết quả ý định của kiểu Instrumentation.ActivityResult . Chúng tôi có thể tạo mục đích sơ khai mới và đặt kết quả theo cách thủ công như được chỉ định bên dưới,
// 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);
Mã hoàn chỉnh để kiểm tra xem ứng dụng liên hệ có được mở đúng cách hay không như sau:
@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)));
}
Ở đây, chúng tôi đã tạo một ý định mới và đặt giá trị trả về (khi gọi ý định) làm mục nhập đầu tiên của danh sách liên hệ, nội dung: //com.android.contacts/data/1 . Sau đó, chúng tôi đã thiết lập phương thức dự định để giả lập ý định mới được tạo thay cho danh sách liên hệ. Nó đặt và gọi ý định mới được tạo của chúng tôi khi gói, com.android.contacts được gọi và mục nhập đầu tiên mặc định của danh sách được trả về. Sau đó, chúng tôi thực hiện hành động click () để bắt đầu mục đích giả và cuối cùng kiểm tra xem số điện thoại từ việc gọi ra mục đích giả và số của mục nhập đầu tiên trong danh sách liên hệ có giống nhau hay không.
Có bất kỳ vấn đề nhập nào bị thiếu, sau đó khắc phục các vấn đề nhập đó bằng cách sử dụng tùy chọn Alt + Enter do android studio cung cấp hoặc nếu không, hãy bao gồm các câu lệnh nhập bên dưới,
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.*;
Thêm quy tắc dưới đây trong lớp thử nghiệm để cung cấp quyền đọc danh sách liên hệ -
@Rule
public GrantPermissionRule permissionRule =
GrantPermissionRule.grant(Manifest.permission.READ_CONTACTS);
Thêm tùy chọn bên dưới vào tệp kê khai ứng dụng, AndroidManifest.xml -
<uses-permission android:name = "android.permission.READ_CONTACTS" />
Bây giờ, hãy đảm bảo danh sách liên hệ có ít nhất một mục nhập và sau đó chạy thử nghiệm bằng menu ngữ cảnh của Android Studio.