Bu belgede, sık kullanılan otomatik test görevlerinin Espresso API kullanılarak nasıl tamamlanacağı açıklanmaktadır.
Espresso API'si, test yazarlarını bir kullanıcının uygulamayla etkileşimde bulunurken, kullanıcı arayüzü öğelerini bulup onlarla etkileşimde bulunurken neler yapabileceği konusunda düşünmeye teşvik eder. Aynı zamanda çerçeve, etkinliklere ve uygulama görünümlerine doğrudan erişimi engeller, çünkü bu nesnelere bağlı kalmak ve bunları kullanıcı arayüzü iş parçacığı dışında çalıştırmak testteki düşüşün ana kaynaklarından biridir. Bu nedenle, Espresso API'de getView()
ve getCurrentActivity()
gibi yöntemler görmezsiniz.
Kendi ViewAction
ve ViewAssertion
alt sınıflarınızı uygulayarak görünümler üzerinde güvenle çalışmaya devam edebilirsiniz.
API bileşenleri
Espresso'nun ana bileşenleri şunlardır:
- Espresso: Görüntülemelerle etkileşimlere giriş noktasıdır (
onView()
veonData()
üzerinden). Ayrıca herhangi bir görünüme bağlı olması gerekmeyen API'leri de (ör.pressBack()
) gösterir. - ViewMatchers –
Matcher<? super View>
arayüzünü uygulayan nesne koleksiyonu. Mevcut görünüm hiyerarşisinde bir görünümü bulmak için bunlardan birini veya daha fazlasınıonView()
yöntemine aktarabilirsiniz. - ViewActions –
ViewInteraction.perform()
yöntemine aktarılabilenclick()
gibiViewAction
nesne koleksiyonu. - ViewAssertions:
ViewInteraction.check()
yöntemi ile iletilebilecekViewAssertion
nesne koleksiyonu. Çoğu zaman, şu anda seçili görünümün durumunu doğrulamak için bir Görünüm eşleştirici kullanan eşleşme onayından yararlanırsınız.
Örnek:
Kotlin
// withId(R.id.my_view) is a ViewMatcher // click() is a ViewAction // matches(isDisplayed()) is a ViewAssertion onView(withId(R.id.my_view)) .perform(click()) .check(matches(isDisplayed()))
Java
// withId(R.id.my_view) is a ViewMatcher // click() is a ViewAction // matches(isDisplayed()) is a ViewAssertion onView(withId(R.id.my_view)) .perform(click()) .check(matches(isDisplayed()));
Bir görünüm bulun
Durumların büyük çoğunluğunda, onView()
yöntemi, mevcut görünüm hiyerarşisinde yalnızca bir görünümle eşleşmesi beklenen bir hamcrest eşleştirici kullanır. Eşleştiriciler güçlüdür ve bunları Mockito veya JUnit ile kullanan kişilere alışıktır. Hamcrest eşleyicilere aşina değilseniz bu sunuya hızlıca göz atmanızı öneririz.
Genellikle, istenen görünüm için benzersiz bir R.id
bulunur ve basit bir withId
eşleştirici, görünüm aramasını daraltır. Bununla birlikte, test geliştirme zamanında R.id
belirleyemediğiniz birçok geçerli durum vardır. Örneğin, belirli bir görünümde R.id
bulunmayabilir veya R.id
benzersiz değildir. findViewById()
ile görünüme erişmenin normal yolu çalışmadığı için bu durum, normal araç testlerinin yazılmasını zorlaştırabilir ve karmaşık hale getirebilir. Dolayısıyla, bu görünümü içeren Etkinlik veya Parça'nın gizli üyelerine erişmeniz ya da bilinen R.id
öğesine sahip bir kapsayıcı bulmanız ve belirli görünüm için bu kapsayıcının içeriğine gitmeniz gerekebilir.
Espresso, mevcut ViewMatcher
nesnelerini veya kendi özel nesnelerinizi kullanarak görünümü daraltmanıza olanak tanıyarak bu sorunu kolayca çözer.
R.id
özelliğine göre bir görünüm bulmak onView()
işlevini çağırmak kadar basittir:
Kotlin
onView(withId(R.id.my_view))
Java
onView(withId(R.id.my_view));
Bazen R.id
değerleri birden fazla görünüm arasında paylaşılır. Bu durumda belirli bir R.id
kullanma girişimi size AmbiguousViewMatcherException
gibi bir istisna verir. İstisna mesajı, mevcut görünüm hiyerarşisini metinle temsil eder. Bu temsilde, benzersiz olmayan R.id
ile eşleşen görünümleri arayıp bulabilirsiniz:
java.lang.RuntimeException: androidx.test.espresso.AmbiguousViewMatcherException This matcher matches multiple views in the hierarchy: (withId: is <123456789>) ... +----->SomeView{id=123456789, res-name=plus_one_standard_ann_button, visibility=VISIBLE, width=523, height=48, has-focus=false, has-focusable=true, window-focus=true, is-focused=false, is-focusable=false, enabled=true, selected=false, is-layout-requested=false, text=, root-is-layout-requested=false, x=0.0, y=625.0, child-count=1} ****MATCHES**** | +------>OtherView{id=123456789, res-name=plus_one_standard_ann_button, visibility=VISIBLE, width=523, height=48, has-focus=false, has-focusable=true, window-focus=true, is-focused=false, is-focusable=true, enabled=true, selected=false, is-layout-requested=false, text=Hello!, root-is-layout-requested=false, x=0.0, y=0.0, child-count=1} ****MATCHES****
Görünümlerin çeşitli özelliklerini incelediğinizde benzersiz şekilde tanımlanabilir mülkler görebilirsiniz. Yukarıdaki örnekte, görünümlerden birinde "Hello!"
metni bulunmaktadır. Kombinasyon eşleştiricileri kullanarak aramanızı daraltmak için bunu kullanabilirsiniz:
Kotlin
onView(allOf(withId(R.id.my_view), withText("Hello!")))
Java
onView(allOf(withId(R.id.my_view), withText("Hello!")));
Ayrıca eşleştiricilerin hiçbirini tersine çevirmemeyi de seçebilirsiniz:
Kotlin
onView(allOf(withId(R.id.my_view), not(withText("Unwanted"))))
Java
onView(allOf(withId(R.id.my_view), not(withText("Unwanted"))));
Espresso tarafından sağlanan görünüm eşleştiriciler için ViewMatchers
adresini inceleyin.
Dikkat edilmesi gereken noktalar
- İyi çalışan bir uygulamada, kullanıcının etkileşimde bulunabileceği tüm görünümler açıklayıcı metin veya bir içerik açıklaması içermelidir. Daha fazla ayrıntı için Uygulamaları daha erişilebilir hale getirme bölümüne bakın.
withText()
veyawithContentDescription()
kullanarak bir aramayı daraltamıyorsanız bu durumu bir erişilebilirlik hatası olarak ele alabilirsiniz. - Aradığınız görünümü bulan, en az açıklayıcı nitelikteki eşleştiriciyi kullanın. Çerçeveyi gereğinden fazla iş yapmaya zorlayacağından, fazla betimleme yapmayın. Örneğin, bir görünüm metninden benzersiz bir şekilde tanımlanabilirse görünümün de
TextView
üzerinden atanabilir olduğunu belirtmeniz gerekmez. Çoğu görüntüleme içinR.id
yeterli olacaktır. - Hedef görünüm bir
AdapterView
içindeyse (ör.ListView
,GridView
veyaSpinner
)onView()
yöntemi çalışmayabilir. Bu durumlarda bunun yerineonData()
kullanmanız gerekir.
Bir görünümde işlem gerçekleştirme
Hedef görünüm için uygun bir eşleştirici bulduğunuzda, gerçekleştirme yöntemini kullanarak bunun üzerinde ViewAction
örnekleri gerçekleştirebilirsiniz.
Örneğin, görünümü tıklamak için:
Kotlin
onView(...).perform(click())
Java
onView(...).perform(click());
Tek bir gerçekleştirme çağrısıyla birden fazla işlem yürütebilirsiniz:
Kotlin
onView(...).perform(typeText("Hello"), click())
Java
onView(...).perform(typeText("Hello"), click());
Üzerinde çalıştığınız görünüm bir ScrollView
(dikey veya yatay) içinde yer alıyorsa scrollTo()
ile görünümün görüntülenmesini gerektiren click()
ve typeText()
gibi önceki işlemleri değerlendirin. Bu şekilde, diğer işleme geçmeden önce görünümün görüntülenmesi sağlanır:
Kotlin
onView(...).perform(scrollTo(), click())
Java
onView(...).perform(scrollTo(), click());
Espresso'nun sağladığı görüntüleme işlemleri için ViewActions
adresine bakın.
Onayları görüntüleme
Onaylar, şu anda seçili olan görünüme check()
yöntemi ile uygulanabilir. En çok kullanılan onaylama, matches()
onaylamadır. Seçili olan görünümün durumunu doğrulamak için bir ViewMatcher
nesnesi kullanır.
Örneğin, bir görünümde "Hello!"
metninin olup olmadığını kontrol etmek için:
Kotlin
onView(...).check(matches(withText("Hello!")))
Java
onView(...).check(matches(withText("Hello!")));
"Hello!"
içeriğinin görünüm içeriği olduğunu iddia etmek istiyorsanız aşağıdaki durum kötü uygulama olarak kabul edilir:
Kotlin
// Don't use assertions like withText inside onView. onView(allOf(withId(...), withText("Hello!"))).check(matches(isDisplayed()))
Java
// Don't use assertions like withText inside onView. onView(allOf(withId(...), withText("Hello!"))).check(matches(isDisplayed()));
Diğer yandan, "Hello!"
metnini içeren bir görünümün görünür olduğunu, örneğin görünümlerin görünürlük işareti değişikliğinden sonra olduğunu iddia etmek istiyorsanız kod kullanılabilir.
Onaylama basit testini görüntüle
Bu örnekte SimpleActivity
, bir Button
ve bir TextView
içeriyor. Düğme tıklandığında TextView
içeriği "Hello Espresso!"
olarak değişir.
Bunu Espresso ile şu şekilde test edebilirsiniz:
Düğmeyi tıklayın
İlk adım, düğmeyi bulmanıza yardımcı olacak bir özellik aramaktır. SimpleActivity
içindeki düğmede beklendiği gibi benzersiz bir R.id
var.
Kotlin
onView(withId(R.id.button_simple))
Java
onView(withId(R.id.button_simple));
Şimdi tıklamayı gerçekleştirmek için:
Kotlin
onView(withId(R.id.button_simple)).perform(click())
Java
onView(withId(R.id.button_simple)).perform(click());
TextView metnini doğrulama
Doğrulanacak metni içeren TextView
de benzersiz bir R.id
öğesine sahip:
Kotlin
onView(withId(R.id.text_simple))
Java
onView(withId(R.id.text_simple));
Şimdi içerik metnini doğrulamak için:
Kotlin
onView(withId(R.id.text_simple)).check(matches(withText("Hello Espresso!")))
Java
onView(withId(R.id.text_simple)).check(matches(withText("Hello Espresso!")));
Bağdaştırıcı görünümlerinde yüklenen verileri kontrol etme
AdapterView
, verilerini bir Bağdaştırıcıdan dinamik olarak yükleyen özel bir widget türüdür. En yaygın AdapterView
örneği ListView
şeklindedir. LinearLayout
gibi statik widget'ların aksine, mevcut görünüm hiyerarşisine AdapterView
alt öğelerinin yalnızca bir alt kümesi yüklenebilir. Basit bir onView()
araması, o anda yüklü olmayan görünümleri bulmaz.
Espresso, söz konusu bağdaştırıcı öğesinin ilk olarak yüklenmesini sağlayan ayrı bir onData()
giriş noktası sağlar. Bu giriş noktası, söz konusu bağdaştırıcı öğesini veya alt öğeleri üzerinde çalıştırmadan önce odak noktasına getirir.
Uyarı: AdapterView
özel uygulamaları, devralma sözleşmelerini (özellikle de getItem()
API'yi) bozarsa onData()
yöntemi ile ilgili sorunlara yol açabilir. Bu gibi durumlarda en iyi yaklaşım, uygulama kodunuzu yeniden düzenlemektir. Bunu yapamazsanız eşleşen bir özel AdapterViewProtocol
uygulayabilirsiniz. Daha fazla bilgi için Espresso'nun sunduğu varsayılan
AdapterViewProtocols
sınıfına göz atın.
Bağdaştırıcı görünümü basit testi
Bu basit testte onData()
ürününün nasıl kullanılacağı gösterilmektedir. SimpleActivity
, kahve içecek türlerini temsil eden birkaç öğeye sahip bir Spinner
içeriyor. Bir öğe seçildiğinde "One %s a day!"
olarak değişen bir TextView
bulunur ve burada %s
, seçilen öğeyi temsil eder.
Bu testin amacı Spinner
öğesini açmak, belirli bir öğeyi seçmek ve TextView
öğesinin bu öğeyi içerdiğini doğrulamaktır. Spinner
sınıfı AdapterView
temel alınarak belirlendiğinden, öğeyi eşleştirmek için onView()
yerine onData()
kullanılması önerilir.
Öğe seçimini aç
Kotlin
onView(withId(R.id.spinner_simple)).perform(click())
Java
onView(withId(R.id.spinner_simple)).perform(click());
Öğe seçme
Öğe seçimi için Spinner
, içeriğiyle bir ListView
oluşturur.
Bu görünüm çok uzun olabilir ve öğe, görünüm hiyerarşisine katkıda bulunmayabilir. onData()
kullanarak istediğimiz öğeyi görünüm hiyerarşisine dahil ederiz. Spinner
içindeki öğeler dize olduğundan "Americano"
Dizesine eşit bir öğeyi eşleştirmek istiyoruz:
Kotlin
onData(allOf(`is`(instanceOf(String::class.java)), `is`("Americano"))).perform(click())
Java
onData(allOf(is(instanceOf(String.class)), is("Americano"))).perform(click());
Metnin doğru olduğunu onaylayın
Kotlin
onView(withId(R.id.spinnertext_simple)) .check(matches(withText(containsString("Americano"))))
Java
onView(withId(R.id.spinnertext_simple)) .check(matches(withText(containsString("Americano"))));
Hata ayıklama
Bir test başarısız olduğunda Espresso yararlı hata ayıklama bilgileri sağlar:
Günlük kaydı
Espresso, tüm görüntüleme işlemlerini logcat'e kaydeder. Örneğin:
ViewInteraction: Performing 'single click' action on view with text: Espresso
Görünüm hiyerarşisi
onView()
başarısız olduğunda Espresso, istisna mesajındaki görünüm hiyerarşisini yazdırır.
onView()
hedef görünümü bulamazsaNoMatchingViewException
gönderilir. Eşleyicinin neden hiçbir görünümle eşleşmediğini analiz etmek için istisna dizesindeki görünüm hiyerarşisini inceleyebilirsiniz.onView()
, belirtilen eşleştiriciyle eşleşen birden fazla görünüm bulursaAmbiguousViewMatcherException
atılır. Görünüm hiyerarşisi yazdırılır ve eşleşen tüm görünümlerMATCHES
etiketiyle işaretlenir:
java.lang.RuntimeException: androidx.test.espresso.AmbiguousViewMatcherException This matcher matches multiple views in the hierarchy: (withId: is <123456789>) ... +----->SomeView{id=123456789, res-name=plus_one_standard_ann_button, visibility=VISIBLE, width=523, height=48, has-focus=false, has-focusable=true, window-focus=true, is-focused=false, is-focusable=false, enabled=true, selected=false, is-layout-requested=false, text=, root-is-layout-requested=false, x=0.0, y=625.0, child-count=1} ****MATCHES**** | +------>OtherView{id=123456789, res-name=plus_one_standard_ann_button, visibility=VISIBLE, width=523, height=48, has-focus=false, has-focusable=true, window-focus=true, is-focused=false, is-focusable=true, enabled=true, selected=false, is-layout-requested=false, text=Hello!, root-is-layout-requested=false, x=0.0, y=0.0, child-count=1} ****MATCHES****
Karmaşık bir görünüm hiyerarşisi veya widget'ların beklenmedik davranışları üzerinde çalışırken açıklama için Android Studio'daki Hiyerarşi Görüntüleyici'yi kullanmak her zaman faydalı olur.
Bağdaştırıcı görünümü uyarıları
Espresso, kullanıcıları AdapterView
widget'larının varlığı konusunda uyarıyor. Bir onView()
işlemi NoMatchingViewException
gönderdiğinde ve görünüm hiyerarşisinde AdapterView
widget'lar olduğunda, en yaygın çözüm onData()
kullanmaktır.
Özel durum mesajı, bağdaştırıcı görünümlerinin listesini içeren bir uyarı içerir.
Bu bilgileri, hedef görünümü yüklemek üzere onData()
yöntemini çağırmak için kullanabilirsiniz.
Ek kaynaklar
Android testlerinde Espresso'yu kullanma hakkında daha fazla bilgi için aşağıdaki kaynaklara bakın.
Sana Özel
- CustomMatcherSample:
Espresso'nun, bir
EditText
nesnesinin ipucu özelliğiyle eşleşecek şekilde nasıl genişletileceğini gösterir. - RecyclerViewSample:
Espresso için
RecyclerView
işlem. - (devamı...)