Bu dokümanda, yaygın olarak kullanılan çeşitli Espresso testlerinin nasıl oluşturulacağı açıklanmaktadır.
Başka bir görünümün yanındaki görünümü eşleştirme
Bir düzen, kendi başına benzersiz olmayan belirli görünümleri içerebilir. Örneğin,
Örneğin, kişiler tablosundaki tekrar eden bir arama düğmesi aynı
R.id
, aynı metni içerir ve diğer çağrıyla aynı özelliklere sahiptir
düğmeleriyle görünmesini sağlayabilirsiniz.
Örneğin, bu etkinlikte, "7"
metnini içeren görünüm birden çok yerde tekrar ediyor
satır:
Benzersiz olmayan görünüm, çoğu zaman bulunan benzersiz bir etiketle eşleştirilir.
(ör. arama düğmesinin yanındaki kişinin adı) tıklayın. Böyle durumlarda
seçiminizi daraltmak için hasSibling()
eşleştiriciyi kullanabilirsiniz:
onView(allOf(withText("7"), hasSibling(withText("item: 0"))))
.perform(click())
onView(allOf(withText("7"), hasSibling(withText("item: 0"))))
.perform(click());
İşlem çubuğunun içindeki bir görünümü eşleştirme
ActionBarTestActivity
üzerinde iki farklı işlem çubuğu vardır: normal
işlem çubuğu ve seçenekler menüsünden oluşturulan bağlamsal bir işlem çubuğu. Her ikisi
işlem çubuklarında her zaman görünen bir öğe ve yalnızca
görünür. Bir öğe tıklandığında, metin görünümü
Tıklanan öğenin içeriği.
Gösterildiği gibi her iki işlem çubuğunda da görünür simgeleri eşleştirmek kolaydır. aşağıdaki kod snippet'inde:
fun testClickActionBarItem() {
// We make sure the contextual action bar is hidden.
onView(withId(R.id.hide_contextual_action_bar))
.perform(click())
// Click on the icon - we can find it by the r.Id.
onView(withId(R.id.action_save))
.perform(click())
// Verify that we have really clicked on the icon
// by checking the TextView content.
onView(withId(R.id.text_action_bar_result))
.check(matches(withText("Save")))
}
public void testClickActionBarItem() {
// We make sure the contextual action bar is hidden.
onView(withId(R.id.hide_contextual_action_bar))
.perform(click());
// Click on the icon - we can find it by the r.Id.
onView(withId(R.id.action_save))
.perform(click());
// Verify that we have really clicked on the icon
// by checking the TextView content.
onView(withId(R.id.text_action_bar_result))
.check(matches(withText("Save")));
}
Kod, içeriğe dayalı işlem çubuğu için aynı görünür:
fun testClickActionModeItem() {
// Make sure we show the contextual action bar.
onView(withId(R.id.show_contextual_action_bar))
.perform(click())
// Click on the icon.
onView((withId(R.id.action_lock)))
.perform(click())
// Verify that we have really clicked on the icon
// by checking the TextView content.
onView(withId(R.id.text_action_bar_result))
.check(matches(withText("Lock")))
}
public void testClickActionModeItem() {
// Make sure we show the contextual action bar.
onView(withId(R.id.show_contextual_action_bar))
.perform(click());
// Click on the icon.
onView((withId(R.id.action_lock)))
.perform(click());
// Verify that we have really clicked on the icon
// by checking the TextView content.
onView(withId(R.id.text_action_bar_result))
.check(matches(withText("Lock")));
}
Taşma menüsündeki öğeleri tıklamak, normal işlem için biraz daha karmaşıktır çubuğunu, tüm cihazlarda bulunan donanım taşma menüsü düğmesini seçenekler menüsünde taşan öğeler vardır ve bazı cihazlarda menü düğmesi gibi görünür. Neyse ki Espresso bu konuda bizim için çok değerli.
Normal işlem çubuğu için:
fun testActionBarOverflow() {
// Make sure we hide the contextual action bar.
onView(withId(R.id.hide_contextual_action_bar))
.perform(click())
// Open the options menu OR open the overflow menu, depending on whether
// the device has a hardware or software overflow menu button.
openActionBarOverflowOrOptionsMenu(
ApplicationProvider.getApplicationContext<Context>())
// Click the item.
onView(withText("World"))
.perform(click())
// Verify that we have really clicked on the icon by checking
// the TextView content.
onView(withId(R.id.text_action_bar_result))
.check(matches(withText("World")))
}
public void testActionBarOverflow() {
// Make sure we hide the contextual action bar.
onView(withId(R.id.hide_contextual_action_bar))
.perform(click());
// Open the options menu OR open the overflow menu, depending on whether
// the device has a hardware or software overflow menu button.
openActionBarOverflowOrOptionsMenu(
ApplicationProvider.getApplicationContext());
// Click the item.
onView(withText("World"))
.perform(click());
// Verify that we have really clicked on the icon by checking
// the TextView content.
onView(withId(R.id.text_action_bar_result))
.check(matches(withText("World")));
}
Donanım taşma menü düğmesi bulunan cihazlarda bu şekilde görünür:
Bağlamsal işlem çubuğu için bunu tekrar yapmak çok kolaydır:
fun testActionModeOverflow() {
// Show the contextual action bar.
onView(withId(R.id.show_contextual_action_bar))
.perform(click())
// Open the overflow menu from contextual action mode.
openContextualActionModeOverflowMenu()
// Click on the item.
onView(withText("Key"))
.perform(click())
// Verify that we have really clicked on the icon by
// checking the TextView content.
onView(withId(R.id.text_action_bar_result))
.check(matches(withText("Key")))
}
}
public void testActionModeOverflow() {
// Show the contextual action bar.
onView(withId(R.id.show_contextual_action_bar))
.perform(click());
// Open the overflow menu from contextual action mode.
openContextualActionModeOverflowMenu();
// Click on the item.
onView(withText("Key"))
.perform(click());
// Verify that we have really clicked on the icon by
// checking the TextView content.
onView(withId(R.id.text_action_bar_result))
.check(matches(withText("Key")));
}
}
Bu örneklere ilişkin kodun tamamını görmek için
GitHub'da ActionBarTest.java
örneği.
Bir görünümün görüntülenmediğini iddia etme
Bir dizi işlem gerçekleştirdikten sonra kesinlikle
durumunu kontrol edin. Bu bazen olumsuz bir durum olabilir; örneğin
olmadığını gösterir. Herhangi bir hamcrest görünümünü çevirebileceğinizi unutmayın.
eşleştiriciyi ViewAssertions.matches()
kullanarak bir ViewAssertion
olarak ayarlayın.
Aşağıdaki örnekte, isDisplayed()
eşleştiriciyi alıp bunu
standart not()
eşleştirici:
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import androidx.test.espresso.matcher.ViewMatchers.withId
import org.hamcrest.Matchers.not
onView(withId(R.id.bottom_left))
.check(matches(not(isDisplayed())))
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static org.hamcrest.Matchers.not;
onView(withId(R.id.bottom_left))
.check(matches(not(isDisplayed())));
Yukarıdaki yaklaşım, görünüm hâlâ hiyerarşinin bir parçasıysa işe yarar. Eğer
değil, bir NoMatchingViewException
alırsınız ve kullanmak için
ViewAssertions.doesNotExist()
.
Bir görünümün mevcut olmadığını iddia edin
Görünüm, görünüm hiyerarşisinden kaldırılırsa (bu durum,
başka bir etkinliğe geçişe neden olduysa,
ViewAssertions.doesNotExist()
:
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.assertion.ViewAssertions.doesNotExist
import androidx.test.espresso.matcher.ViewMatchers.withId
onView(withId(R.id.bottom_left))
.check(doesNotExist())
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
onView(withId(R.id.bottom_left))
.check(doesNotExist());
Bir veri öğesinin bağdaştırıcıda olmadığını iddia etme
Belirli bir veri öğesinin AdapterView
sınırları içinde olmadığını kanıtlamak için aşağıdakileri yapmanız gerekir
bazı değişiklikler yapıyoruz. İlgilendiğimiz AdapterView
ürününü bulmamız gerekiyor
elindeki verileri sorgulamaya gelir. onData()
kullanmamız gerekmiyor.
Bunun yerine, AdapterView
öğesini bulmak için onView()
operatörünü, ardından başka bir tane kullanırız.
görünüm içindeki veriler üzerinde çalışmak için "eşleştirici"yi kullanın.
Öncelikle eşleştirici:
private fun withAdaptedData(dataMatcher: Matcher<Any>): Matcher<View> {
return object : TypeSafeMatcher<View>() {
override fun describeTo(description: Description) {
description.appendText("with class name: ")
dataMatcher.describeTo(description)
}
public override fun matchesSafely(view: View) : Boolean {
if (view !is AdapterView<*>) {
return false
}
val adapter = view.adapter
for (i in 0 until adapter.count) {
if (dataMatcher.matches(adapter.getItem(i))) {
return true
}
}
return false
}
}
}
private static Matcher<View> withAdaptedData(final Matcher<Object> dataMatcher) {
return new TypeSafeMatcher<View>() {
@Override
public void describeTo(Description description) {
description.appendText("with class name: ");
dataMatcher.describeTo(description);
}
@Override
public boolean matchesSafely(View view) {
if (!(view instanceof AdapterView)) {
return false;
}
@SuppressWarnings("rawtypes")
Adapter adapter = ((AdapterView) view).getAdapter();
for (int i = 0; i < adapter.getCount(); i++) {
if (dataMatcher.matches(adapter.getItem(i))) {
return true;
}
}
return false;
}
};
}
O zaman AdapterView
öğesini bulmak için yalnızca onView()
gerekir:
fun testDataItemNotInAdapter() {
onView(withId(R.id.list))
.check(matches(not(withAdaptedData(withItemContent("item: 168")))))
}
}
@SuppressWarnings("unchecked")
public void testDataItemNotInAdapter() {
onView(withId(R.id.list))
.check(matches(not(withAdaptedData(withItemContent("item: 168")))));
}
}
Ayrıca, "item: 168" değerine eşit olan bir öğe, başarısız olacak bir onayımız var. bağdaştırıcı görünümünde kimlik listesiyle birlikte bulunur.
Örneklemin tamamı için tablodaki testDataItemNotInAdapter()
yöntemine bakın.
AdapterViewTest.java
gidin.
Özel hata işleyici kullan
Espresso'daki varsayılan FailureHandler
değerini özel bir öğeyle değiştirerek
ek veya farklı hata işleme (örneğin, ekran görüntüsü alma veya
hata ayıklama bilgileri ile birlikte ekleyin.
CustomFailureHandlerTest
örneği, özel bir değerin nasıl uygulanacağını gösterir
hata işleyici:
private class CustomFailureHandler(targetContext: Context) : FailureHandler {
private val delegate: FailureHandler
init {
delegate = DefaultFailureHandler(targetContext)
}
override fun handle(error: Throwable, viewMatcher: Matcher<View>) {
try {
delegate.handle(error, viewMatcher)
} catch (e: NoMatchingViewException) {
throw MySpecialException(e)
}
}
}
private static class CustomFailureHandler implements FailureHandler {
private final FailureHandler delegate;
public CustomFailureHandler(Context targetContext) {
delegate = new DefaultFailureHandler(targetContext);
}
@Override
public void handle(Throwable error, Matcher<View> viewMatcher) {
try {
delegate.handle(error, viewMatcher);
} catch (NoMatchingViewException e) {
throw new MySpecialException(e);
}
}
}
Bu hata işleyici,MySpecialException
NoMatchingViewException
ve diğer tüm başarısızlıklarda
DefaultFailureHandler
. CustomFailureHandler
, kayıt için kullanılabilir:
Testin setUp()
yönteminde espresso:
@Throws(Exception::class)
override fun setUp() {
super.setUp()
getActivity()
setFailureHandler(CustomFailureHandler(
ApplicationProvider.getApplicationContext<Context>()))
}
@Override
public void setUp() throws Exception {
super.setUp();
getActivity();
setFailureHandler(new CustomFailureHandler(
ApplicationProvider.getApplicationContext()));
}
Daha fazla bilgi için
FailureHandler
arayüz ve
Espresso.setFailureHandler()
)
Varsayılan olmayan pencereleri hedefle
Android birden çok pencereyi destekler. Normalde bu, kullanıcılar açısından şeffaf bir süreçtir
ve uygulama geliştiricisi tarafından, ancak bazı durumlarda birden fazla pencerenin görünür olması
ana uygulama penceresinin üzerine gelindiğinde otomatik tamamlama
arama widget'ı. Espresso, işleri basitleştirmek için varsayılan olarak buluşsal bir
hangi Window
ile etkileşime girmek istediğinizi tahmin edin. Bu buluşsal yöntem,
her zaman yeterince iyidir. ancak nadiren de olsa her bir kullanıcının hangi
bir etkileşimin hedeflenmesidir. Bunu yapmak için kendi kök pencerenizi sağlayabilirsiniz
eşleştirici veya Root
eşleştirici:
onView(withText("South China Sea"))
.inRoot(withDecorView(not(`is`(getActivity().getWindow().getDecorView()))))
.perform(click())
onView(withText("South China Sea"))
.inRoot(withDecorView(not(is(getActivity().getWindow().getDecorView()))))
.perform(click());
Proje yönetiminde
ViewMatchers
önceden sağlanmış bir dizi
RootMatchers
.
Elbette, istediğiniz zaman kendi Matcher
nesnenizi uygulayabilirsiniz.
Çoklu pencere testi için MultipleWindowTest'e örnek bulabilirsiniz.
Liste görünümünde üstbilgi veya altbilgi eşleştirme
ListViews
için üstbilgi ve altbilgiler, addHeaderView()
ve
addFooterView()
yöntem. Espresso.onData()
uygulamasının hangi veri nesnesini bilmesini sağlamak için
eşleşmesini sağlamak için ikinci parametre olarak önceden ayarlanmış bir veri nesnesi değerini
addHeaderView()
ve addFooterView()
arasında. Örnek:
const val FOOTER = "FOOTER"
...
val footerView = layoutInflater.inflate(R.layout.list_item, listView, false)
footerView.findViewById<TextView>(R.id.item_content).text = "count:"
footerView.findViewById<TextView>(R.id.item_size).text
= data.size.toString
listView.addFooterView(footerView, FOOTER, true)
public static final String FOOTER = "FOOTER";
...
View footerView = layoutInflater.inflate(R.layout.list_item, listView, false);
footerView.findViewById<TextView>(R.id.item_content).setText("count:");
footerView.findViewById<TextView>(R.id.item_size).setText(String.valueOf(data.size()));
listView.addFooterView(footerView, FOOTER, true);
Ardından, altbilgi için bir eşleştirici yazabilirsiniz:
import org.hamcrest.Matchers.allOf
import org.hamcrest.Matchers.instanceOf
import org.hamcrest.Matchers.`is`
fun isFooter(): Matcher<Any> {
return allOf(`is`(instanceOf(String::class.java)),
`is`(LongListActivity.FOOTER))
}
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
@SuppressWarnings("unchecked")
public static Matcher<Object> isFooter() {
return allOf(is(instanceOf(String.class)), is(LongListActivity.FOOTER));
}
Görünümü bir testte yüklemek çok basittir:
import androidx.test.espresso.Espresso.onData
import androidx.test.espresso.action.ViewActions.click
import androidx.test.espresso.sample.LongListMatchers.isFooter
fun testClickFooter() {
onData(isFooter())
.perform(click())
// ...
}
import static androidx.test.espresso.Espresso.onData;
import static androidx.test.espresso.action.ViewActions.click;
import static androidx.test.espresso.sample.LongListMatchers.isFooter;
public void testClickFooter() {
onData(isFooter())
.perform(click());
// ...
}
Şu sayfadaki kod örneğine göz atın: testClickFooter()
yönteminde bulunan:
AdapterViewTest.java
bulabilirsiniz.