Espresso Test Çerçevesi - Amaçlar

Android Intent, dahili (ürün listesi ekranından bir ürün ayrıntı ekranını açma) veya harici (arama yapmak için bir çevirici açma gibi) yeni etkinliği açmak için kullanılır. Dahili amaç etkinliği, espresso test çerçevesi tarafından şeffaf bir şekilde ele alınır ve kullanıcı tarafından herhangi bir özel çalışma gerektirmez. Bununla birlikte, dış faaliyete başvurmak gerçekten bir zorluktur çünkü kapsamımızın, yani test edilen uygulamanın dışında kalır. Kullanıcı harici bir uygulamayı çağırdığında ve test edilen uygulamadan çıktığında, kullanıcının önceden tanımlanmış eylem sırası ile uygulamaya geri dönme şansı oldukça azdır. Bu nedenle, uygulamayı test etmeden önce kullanıcı eylemini üstlenmemiz gerekir. Espresso, bu durumu ele almak için iki seçenek sunar. Bunlar aşağıdaki gibidir,

amaçlanan

Bu, kullanıcının test edilen uygulamadan doğru niyetin açıldığından emin olmasını sağlar.

niyet

Bu, kullanıcının kameradan fotoğraf çekmek, kişi listesinden bir numara çevirmek vb. Gibi harici bir etkinlikle alay etmesine ve önceden tanımlanmış değerler kümesiyle uygulamaya geri dönmesine olanak tanır (gerçek görüntü yerine kameradan önceden tanımlanmış görüntü gibi) .

Kurmak

Espresso, bir eklenti kitaplığı aracılığıyla amaç seçeneğini destekler ve kitaplığın uygulamanın gradle dosyasında yapılandırılması gerekir. Yapılandırma seçeneği aşağıdaki gibidir,

dependencies {
   // ...
   androidTestImplementation 'androidx.test.espresso:espresso-intents:3.1.1'
}

amaçlanan ()

Espresso niyet eklentisi, çağrılan amacın beklenen amaç olup olmadığını kontrol etmek için özel eşleştiriciler sağlar. Sağlanan eşleştiriciler ve eşleştiricilerin amacı aşağıdaki gibidir,

hasAction

Bu, niyet eylemini kabul eder ve belirtilen amaç ile eşleşen bir eşleştirici döndürür.

hasData

Bu, verileri kabul eder ve onu çağırırken amaca sağlanan verilerle eşleşen bir eşleştirici döndürür.

Paketlemek

Bu, amaç paket adını kabul eder ve çağrılan amacın paket adıyla eşleşen bir eşleştirici döndürür.

Şimdi, kavramı anlamak için amaçlanan () kullanarak yeni bir uygulama oluşturalım ve uygulamayı harici aktivite için test edelim .

  • Android stüdyosunu başlatın.

  • Daha önce tartışıldığı gibi yeni bir proje oluşturun ve bunu IntentSampleApp olarak adlandırın.

  • Refactor → Migrate to AndroidX seçenek menüsünü kullanarak uygulamayı AndroidX çerçevesine taşıyın .

  • Activity_main.xml'i aşağıda gösterildiği gibi değiştirerek bir metin kutusu, kişi listesini açmak için bir düğme ve bir arama çevirmek için başka bir tane oluşturun ,

<?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>
  • Ayrıca, aşağıdaki öğeyi strings.xml kaynak dosyasına ekleyin ,

<string name = "phone_number">Phone number</string>
<string name = "call">Call</string>
<string name = "call_contact">Select from contact list</string>
  • Şimdi, aşağıdaki kodu onCreate yönteminin altındaki ana etkinliğe ( MainActivity.java ) ekleyin .

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
}

Burada, butonu id, call_contact_button ile kontak listesini açmak için ve id butonu , çağrı çevirmek için buton programladık .

  • Statik değişken ekle REQUEST_CODE içinde MainActivity aşağıda gösterildiği gibi sınıf,

public class MainActivity extends AppCompatActivity {
   // ...
   private static final int REQUEST_CODE = 1;
   // ...
}
  • Şimdi MainActivity sınıfına onActivityResult yöntemini aşağıdaki gibi ekleyin ,

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);
         }
      }
   };
   // ...
}

Burada, bir kullanıcı call_contact_button düğmesini kullanarak kişi listesini açtıktan ve bir kişi seçtikten sonra uygulamaya döndüğünde onActivityResult çağrılacaktır . Bir kez onActivityResult yöntemi çağrılır, bu kullanıcının seçtiği kişiyi, irtibat numarasını bulmak ve metin kutusuna ayarlayın alır.

  • Uygulamayı çalıştırın ve her şeyin yolunda olduğundan emin olun. Niyet örneği Uygulamasının son görünümü aşağıda gösterildiği gibidir,

  • Şimdi, uygulamanın gradle dosyasında espresso amacını aşağıda gösterildiği gibi yapılandırın,

dependencies {
   // ...
   androidTestImplementation 'androidx.test.espresso:espresso-intents:3.1.1'
}
  • Android Studio tarafından sağlanan Şimdi Senkronize Et menü seçeneğini tıklayın . Bu, amaç testi kitaplığını indirecek ve uygun şekilde yapılandıracaktır.

  • ExampleInstrumentedTest.java dosyasını açın ve normalde kullanılan AndroidTestRule yerine IntentsTestRule'u ekleyin . IntentTestRule , niyet testini işlemek için özel bir kuraldır.

public class ExampleInstrumentedTest {
   // ... code
   @Rule
   public IntentsTestRule<MainActivity> mActivityRule =
   new IntentsTestRule<>(MainActivity.class);
   // ... code
}
  • Test telefon numarasını ve çevirici paket adını aşağıdaki gibi ayarlamak için iki yerel değişken ekleyin,

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 studio tarafından sağlanan Alt + Enter seçeneğini kullanarak içe aktarma sorunlarını düzeltin veya aşağıdaki içe aktarma ifadelerini ekleyin,

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.*;
  • Çeviricinin doğru şekilde çağrılıp çağrılmadığını test etmek için aşağıdaki test durumunu ekleyin,

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
}

Burada hasAction , hasData ve toPackage eşleştiricileri, yalnızca tüm eşleştiriciler geçilirse başarılı olmak için allOf eşleştiriciyle birlikte kullanılır .

  • Şimdi, Android stüdyosundaki içerik menüsünden ExampleInstrumentedTest'i çalıştırın .

niyet eden ()

Espresso, () harici bir niyet eylemiyle dalga geçmeyi amaçlayan özel bir yöntem sağlar . intending () alay edilecek niyetin paket adını kabul eder ve aşağıda belirtildiği gibi alay konusu niyetin nasıl yanıtlanması gerektiğini ayarlamak için bir yanıt yöntemi sağlar .

intending(toPackage("com.android.contacts")).respondWith(result);

Burada, responseWith () Instrumentation.ActivityResult türünün amaç sonucunu kabul eder . Yeni saplama amacı oluşturabilir ve sonucu aşağıda belirtildiği gibi manuel olarak ayarlayabiliriz,

// 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);

Bir iletişim uygulamasının düzgün bir şekilde açılıp açılmadığını test etmek için kodun tamamı aşağıdaki gibidir:

@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)));
}

Burada, yeni bir amaç oluşturduk ve dönüş değerini (amacı çağırırken) kişi listesinin ilk girişi olarak ayarladık , content: //com.android.contacts/data/1 . Ardından , kişi listesi yerine yeni oluşturulan niyetle alay etmek için niyetlenme yöntemini ayarladık . Com.android.contacts paketi çağrıldığında ve listenin varsayılan ilk girişi döndürüldüğünde yeni oluşturulan amacımızı ayarlar ve çağırır . Ardından, sahte amacı başlatmak için click () eylemini başlattık ve son olarak, sahte amacı çağıran telefon numarasının ve kişi listesindeki ilk girişin numarasının aynı olup olmadığını kontrol ettik .

Eksik bir içe aktarma sorunu varsa, bu içe aktarma sorunlarını android studio tarafından sağlanan Alt + Enter seçeneğini kullanarak düzeltin veya aşağıdaki içe aktarma ifadelerini ekleyin,

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.*;

Kişi listesini okuma izni sağlamak için aşağıdaki kuralı test sınıfına ekleyin -

@Rule
public GrantPermissionRule permissionRule =
GrantPermissionRule.grant(Manifest.permission.READ_CONTACTS);

Uygulama manifest dosyasına aşağıdaki seçeneği ekleyin, AndroidManifest.xml -

<uses-permission android:name = "android.permission.READ_CONTACTS" />

Şimdi, kişi listesinde en az bir giriş olduğundan emin olun ve ardından Android Studio'nun içerik menüsünü kullanarak testi çalıştırın.