Espresso 테스트 프레임 워크-인 텐트
Android 인 텐트는 내부 (제품 목록 화면에서 제품 세부 정보 화면 열기) 또는 외부 (예 : 전화를 걸기 위해 다이얼러 열기)의 새 활동을 여는 데 사용됩니다. 내부 의도 활동은 에스프레소 테스트 프레임 워크에 의해 투명하게 처리되며 사용자 측의 특정 작업이 필요하지 않습니다. 그러나 외부 활동을 호출하는 것은 테스트중인 애플리케이션의 범위를 벗어나기 때문에 정말 어려운 일입니다. 사용자가 외부 응용 프로그램을 호출하고 테스트중인 응용 프로그램에서 나가면 사용자가 미리 정의 된 작업 순서를 사용하여 응용 프로그램으로 돌아올 가능성이 다소 적습니다. 따라서 응용 프로그램을 테스트하기 전에 사용자 작업을 가정해야합니다. Espresso는 이러한 상황을 처리 할 수있는 두 가지 옵션을 제공합니다. 다음과 같습니다.
예정된
이를 통해 사용자는 테스트중인 애플리케이션에서 올바른 인 텐트가 열렸는지 확인할 수 있습니다.
의도
이를 통해 사용자는 카메라에서 사진 찍기, 연락처 목록에서 전화 걸기 등과 같은 외부 활동을 조롱하고 미리 정의 된 값 집합 (실제 이미지 대신 카메라에서 미리 정의 된 이미지)을 사용하여 애플리케이션으로 돌아갈 수 있습니다. .
설정
Espresso는 플러그인 라이브러리를 통해 인 텐트 옵션을 지원하며 라이브러리는 애플리케이션의 gradle 파일에서 구성되어야합니다. 구성 옵션은 다음과 같습니다.
dependencies {
// ...
androidTestImplementation 'androidx.test.espresso:espresso-intents:3.1.1'
}
예정된()
Espresso 인 텐트 플러그인은 호출 된 인 텐트가 예상되는 인 텐트인지 확인하는 특수 매처를 제공합니다. 제공되는 매처 및 매처의 목적은 다음과 같습니다.
hasAction
이는 인 텐트 작업을 수락하고 지정된 인 텐트와 일치하는 매처를 반환합니다.
hasData
이것은 데이터를 받아들이고 그것을 호출하는 동안 인 텐트에 제공된 데이터와 일치하는 매처를 반환합니다.
toPackage
이는 인 텐트 패키지 이름을 수락하고 호출 된 인 텐트의 패키지 이름과 일치하는 매처를 반환합니다.
이제 새로운 애플리케이션을 생성하고 의도 된 () 을 사용하여 외부 활동에 대한 애플리케이션을 테스트 하여 개념을 이해 하겠습니다 .
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가있는 버튼 , 전화를 걸 수있는 버튼 을 프로그래밍했습니다 .
아래와 같이 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);
}
}
};
// ...
}
여기서 onActivityResult 는 사용자가 call_contact_button 버튼을 사용하여 연락처 목록을 열고 연락처를 선택한 후 애플리케이션으로 돌아올 때 호출됩니다 . 한때 하여 onActivityResult 메소드가 호출, 사용자 선택한 연락처의 연락처를 찾아 텍스트 상자에 설정을 가져옵니다.
응용 프로그램을 실행하고 모든 것이 정상인지 확인하십시오. Intent 샘플 애플리케이션 의 최종 모습은 아래와 같습니다.
이제 아래와 같이 애플리케이션의 gradle 파일에서 에스프레소 인 텐트를 구성합니다.
dependencies {
// ...
androidTestImplementation 'androidx.test.espresso:espresso-intents:3.1.1'
}
Android Studio에서 제공 하는 Sync Now 메뉴 옵션을 클릭합니다 . 그러면 인 텐트 테스트 라이브러리가 다운로드되고 올바르게 구성됩니다.
열기 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
}
Android 스튜디오에서 제공하는 Alt + Enter 옵션을 사용하여 가져 오기 문제를 수정하거나 아래 import 문을 포함합니다.
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는 외부 인 텐트 작업을 모의하기위한 purposeing () 이라는 특수 메서드를 제공합니다 . 의도 () 목적의 패키지 이름 조롱 할을 받아들이고 방법의 제공 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로 설정했습니다 . 그럼 우리가 설정 하려는 연락처 목록 대신에 새로 만든 의도를 조롱하는 방법. com.android.contacts 패키지 가 호출되고 목록의 기본 첫 번째 항목이 반환 될 때 새로 생성 된 인 텐트를 설정하고 호출합니다 . 그런 다음 click () 액션을 실행하여 모의 인 텐트를 시작하고 마지막으로 모의 인 텐트를 호출 한 전화 번호와 연락처 목록의 첫 번째 항목 번호가 동일한 지 확인합니다.
누락 된 가져 오기 문제가있는 경우 Android 스튜디오에서 제공하는 Alt + Enter 옵션을 사용하여 가져 오기 문제를 수정하거나 아래 import 문을 포함합니다.
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의 컨텍스트 메뉴를 사용하여 테스트를 실행합니다.