Espresso یک فریمورک برای تست خودکار UI برنامههای Android است که توسط تیم Google توسعه یافته و بخشی از AndroidX Test میباشد. برخلاف تستهای ابزاری که کامپوننتهای ایزوله را بررسی میکنند، Espresso با UI واقعی تعامل دارد: دکمهها را فشار میدهد، متن وارد میکند، نمایش عناصر را بررسی میکند. به گفته Google Android Developers، Espresso همگامسازی خودکار با رشته UI را فراهم میکند که نیاز به Thread.sleep() دستی را از بین میبرد.
نکات اصلی
Espresso یک کتابخانه برای نوشتن تستهای UI خودکار Android است که بخشی از Google AndroidX Test میباشد. این کتابخانه APIهایی برای جستجوی عناصر View در صفحه، انجام اقدامات روی آنها (کلیک، ورود، سوایپ) و بررسی وضعیت آنها (نمایش داده میشود، شامل متن است، فعال است) ارائه میدهد.
ویژگی کلیدی Espresso — همگامسازی خودکار با رشته اصلی برنامه است. فریمورک منتظر تکمیل تمام وظایف ناهمگام (کروتینها، AsyncTask، Handler) قبل از انجام بررسی بعدی میماند. این کار تستهای ناپایدار مرتبط با race condition را حذف کرده و تستهای UI را پایدار و قابل اعتماد میسازد — هیچ تستی شامل Thread.sleep() یا حلقههای انتظار نیست.
Espresso از اصل Three-Legged Dog پیروی میکند — تست از سه مرحله تشکیل شده است: یافتن عنصر (ViewMatcher)، انجام عمل (ViewAction)، بررسی نتیجه (ViewAssertion). هر سه مرحله در زنجیره فراخوانی onView().perform().check() نوشته میشوند. این مفهوم تستها را قابل پیشبینی و خوانا میسازد — هر تست به وضوح توضیح میدهد که چه چیزی را جستجو میکند، چه کاری انجام میدهد و چه چیزی را بررسی میکند.
معماری Espresso بر سه مؤلفه استوار است: Espresso (نقطه ورود — متدهای استاتیک onView و onData)، ViewMatchers (جستجوی عناصر)، ViewActions (اقدامات) و ViewAssertions (بررسیها). در داخل، فریمورک از Idling Resource برای همگامسازی با رشته UI استفاده میکند.
یک تست ساده دکمهای را با ID پیدا میکند، کلیک را انجام میدهد و بررسی میکند که متن «تمام» ظاهر شده است. تمام عملیات از دید تست به صورت همزمان انجام میشود — Espresso تضمین میکند که رشته UI پردازش رویداد را قبل از ادامه تست به پایان رسانده است. این کار از طریق مکانیزم انتظار داخلی به دست میآید: onView اجرای تست را مسدود میکند تا زمانی که UI پایدار شود.
@Test
fun buttonClick_showsSuccessText() {
// یافتن دکمه با شناسه و کلیک کردن
onView(withId(R.id.button_submit))
.perform(click())
// بررسی اینکه متن «تمام» نمایش داده میشود
onView(withText("تمام"))
.check(matches(isDisplayed()))
}
برای راهاندازی تست Espresso از ActivityScenario (AndroidX Test) استفاده میشود که Activity را در وضعیت مورد نظر ایجاد میکند — در حال اجرا، متوقف شده، نابود شده. ActivityScenario امکان تست چرخه حیات Activity را علاوه بر UI خالص فراهم میکند. به عنوان مثال، میتوان بررسی کرد که دادهها در چرخش صفحه (بازآفرینی Activity) ذخیره شده و پس از نابودی بازیابی میشوند.
ViewMatchers مجموعهای از متدهای کلاس Espresso.onView هستند که امکان یافتن View در صفحه را بر اساس معیارهای مختلف فراهم میکنند: شناسه منبع (R.id)، متن، راهنمای hint، عنصر والد و سلسلهمراتب. اگر یک matcher نتیجه منحصربهفرد ندهد، matcherها از طریق allOf() ترکیب میشوند.
| Matcher | کاربرد |
|---|---|
| withId(R.id.name) | جستجو بر اساس ID منبع |
| withText("متن") | جستجو بر اساس متن نمایش داده شده |
| withHint("راهنما") | جستجو بر اساس ویژگی hint در EditText |
| isDisplayed() | بررسی نمایش عنصر در صفحه |
| hasSibling(matcher) | جستجو بر اساس عنصر مجاور |
| allOf(m1, m2) | ترکیب چند matcher |
اگر چندین عنصر مشابه در صفحه وجود داشته باشد (مثلاً دو TextView با متنهای مختلف)، ترکیب matcherها از طریق allOf很方便 است: onView(allOf(withId(R.id.title), withText("سلام"))). این کار انتخاب یک عنصر واحد را تضمین میکند. عملگر معکوس — not() — عناصر را از جستجو حذف میکند و hasSibling() عنصر را در کنار عنصر شناخته شده جستجو میکند.
ViewActions اقداماتی هستند که Espresso روی View یافت شده انجام میدهد: click()، typeText()، clearText()، scrollTo()، swipeLeft() و موارد دیگر. اقدامات به متد perform() منتقل میشوند که میتواند چندین عمل را پشت سر هم بپذیرد.
متد perform() vararg ViewAction را میپذیرد که امکان اجرای دنبالهای از اقدامات روی یک عنصر را فراهم میکند: پاک کردن فیلد، وارد کردن متن جدید، بستن صفحهکلید و کلیک دکمه. تمام اقدامات به ترتیب ذکر شده انجام میشوند و Espresso تضمین میکند که عمل قبلی قبل از شروع عمل بعدی کامل شده است.
// ورود متن در EditText و کلیک دکمه
onView(withId(R.id.edit_email))
.perform(
clearText(),
typeText("user@example.com"),
closeSoftKeyboard()
)
onView(withId(R.id.button_login))
.perform(click())
برای عناصر داخل AdapterView (ListView, RecyclerView) از متد onData() به جای onView استفاده میشود. این متد با دادههای آداپتر کار میکند نه با View — عنصر را بر اساس محتوای مدل پیدا میکند و View مربوطه را برای اقدامات بعدی برمیگرداند. onData از matcherهای hamcrest برای جستجوی عنصر بر اساس فیلدهای مدل داده استفاده میکند.
ViewAssertions بررسی میکنند که View در وضعیت مشخصی قرار دارد. متد پایه — matches(matcher) — بررسی میکند که عنصر با matcher داده شده مطابقت دارد. علاوه بر این، Espresso doesNotExist() (عنصر وجود ندارد) و selectedDescendantsMatch() (بررسی عناصر تو در تو) را ارائه میدهد.
رایجترین بررسیها در تستهای UI: عنصر نمایش داده میشود (isDisplayed)، عنصر شامل متن مشخصی است (withText)، عنصر فعال است (isEnabled)، عنصر انتخاب نشده است (isNotChecked). هر بررسی در صورت شکست یک استثنای دقیق با ذکر سلسلهمراتب View در صفحه ایجاد میکند. این کار اشکالزدایی را ساده میکند: در پیام خطا مشخص است که چه عناصری واقعاً در لحظه بررسی در صفحه بودهاند.
اگر بررسیهای استاندارد کافی نباشند، میتوان از طریق رابط ViewAssertion یک بررسی سفارشی ایجاد کرد. assertion سفارشی View را دریافت کرده و میتواند وضعیت آن را برنامهنویسی بررسی کند — مثلاً رنگ متن، حاشیهها یا وضعیت کامپوننت سفارشی که از طریق matcherهای استاندارد قابل دسترسی نیست.
// بررسی: TextView نمایش داده میشود و شامل متن است
onView(withId(R.id.text_welcome))
.check(matches(isDisplayed()))
.check(matches(withText("خوش آمدید")))
// بررسی: عنصر نمایش داده نمیشود
onView(withId(R.id.progress_bar))
.check(doesNotExist())
Idling Resource مکانیزم Espresso برای همگامسازی تست با عملیات ناهمگام است. به طور پیشفرض، Espresso منتظر تکمیل Handler، AsyncTask و کروتینها (از طریق coroutinesIdlingResource) میماند. اگر برنامه کار پسزمینهای را از طریق رشتههای خود یا سرویسهای Callback انجام میدهد، باید یک Idling Resource سفارشی ثبت کرد.
از AndroidX Test 1.4.0 به بعد، Espresso از کروتینها از طریق CoroutinesIdlingResource پشتیبانی میکند. تست به طور خودکار منتظر تکمیل تمام کروتینهای راهاندازی شده قبل از انجام بررسیهای UI میماند. برای سناریوهای پیچیدهتر از CountingIdlingResource استفاده میشود — شمارندهای که در شروع کار افزایش و در پایان کاهش مییابد.
// ثبت IdlingResource برای OkHttp
class OkHttpIdlingResource(
private val client: OkHttpClient
) : IdlingResource {
private var isIdle = true
private var watcher: IdlingResource.ResourceCallback? = null
override fun getName() = "OkHttp"
override fun isIdleNow() = isIdle
override fun registerIdleTransitionCallback(
callback: IdlingResource.ResourceCallback
) {
watcher = callback
}
}
اتصال Espresso در پروژه Android از طریق افزودن وابستگیها در build.gradle سطح ماژول انجام میشود. Espresso بخشی از AndroidX Test است، بنابراین کافی است وابستگیهای هسته Espresso، افزونهها و یکپارچهسازی JUnit را مشخص کنید. تستها در دایرکتوری src/androidTest قرار میگیرند و روی دستگاه فیزیکی یا شبیهساز از طریق AndroidJUnitRunner اجرا میشوند.
حداقل مجموعه وابستگیها شامل espresso-core (هسته)، espresso-contrib (matcherهای اضافی برای RecyclerView، Drawer، Picker) و runner (رunner تست AndroidX) است. تمام تستها روی شبیهساز یا دستگاه فیزیکی از طریق Android Test Orchestrator اجرا میشوند.
// build.gradle.kts (androidTest dependencies)
android {
defaultConfig {
testInstrumentationRunner =
"androidx.test.runner.AndroidJUnitRunner"
}
}
dependencies {
androidTestImplementation("androidx.test.espresso:espresso-core:3.6.1")
androidTestImplementation("androidx.test.espresso:espresso-contrib:3.6.1")
androidTestImplementation("androidx.test:runner:1.6.1")
androidTestImplementation("androidx.test:rules:1.6.1")
}
تستهای Espresso را میتوان از طریق Google Android Test Orchestrator اجرا کرد که هر تست را در یک فرآیند جداگانه ایزوله کرده و وضعیت را بین اجراها پاک میکند. این کار تستهای ناپایدار مرتبط با دادههای باقیمانده از تستهای قبلی را حذف کرده و پایداری را در سرورهای CI افزایش میدهد. برای اجرای موازی از sharding استفاده میشود — توزیع تستها بین چند شبیهساز.
سوالات متداول
Espresso در داخل فرآیند برنامه کار میکند و از همگامسازی خودکار با رشته UI استفاده میکند. UI Automator در سطح سیستم کار میکند، میتواند با برنامههای دیگر تعامل داشته باشد، اما نیاز به مدیریت دستی انتظار دارد.
این یک استعاره از ارائه Google است: تست Espresso روی سه پایه ایستاده است — ViewMatcher (جستجو)، ViewAction(عمل) و ViewAssertion(بررسی). اگر هر یک از آنها حذف شود، تست ثبات خود را از دست میدهد، مانند یک سگ سهپا.
برای RecyclerView از کتابخانه espresso-contrib و متدهای onView(withId(R.id.recycler)).perform(actionOnItemAtPosition(0, click())) استفاده میشود. جایگزین — onData() برای AdapterView یا ViewAction سفارشی برای جستجوی عنصر بر اساس متن داخل RecyclerView. علاوه بر این میتوان از RecyclerViewActions از espresso-contrib برای اسکرول به عنصر و اقدامات روی آن استفاده کرد.
تست ناپایدار (flaky) تستی است که گاهی بدون تغییر کode، به دلیل race condition یا ناهمگامی، شکست میخورد. Espresso این مشکل را از طریق Idling Resource حل میکند — انتظار برای تکمیل تمام وظایف پسزمینه قبل از انجام بررسی.
خود Espresso برای تستهای اسکرینشات طراحی نشده است، اما میتوان آن را با کتابخانههایی مانند Shot یا Paparazzi ترکیب کرد. Espresso UI را به وضعیت مورد نظر میرساند و کتابخانه مقایسه اسکرینشات گرفته و با مرجع مقایسه میکند. این رویکرد تست رگرسیون بصری نامیده میشود و به یافتن تغییرات غیرمنتظره در رابط کاربری کمک میکند.
خلاصه
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.
همچنین بخوانید