پایداری تست بزرگ

ماهیت ناهمزمان برنامه‌ها و چارچوب‌های تلفن همراه اغلب نوشتن تست‌های قابل اعتماد و قابل تکرار را چالش‌برانگیز می‌کند. هنگامی که یک رویداد کاربر تزریق می شود، چارچوب آزمایشی باید منتظر بماند تا برنامه واکنش خود را به آن تمام کند، که می تواند از تغییر متن روی صفحه تا بازآفرینی کامل یک فعالیت متغیر باشد. وقتی تستی رفتار قطعی نداشته باشد، پوسته پوسته است.

چارچوب‌های مدرن مانند Compose یا Espresso با در نظر گرفتن آزمایش طراحی شده‌اند، بنابراین تضمین خاصی وجود دارد که UI قبل از اقدام یا ادعای آزمایشی بعدی بی‌کار باشد. این همگام سازی است.

تست همگام سازی

هنگام اجرای عملیات ناهمزمان یا پس‌زمینه ناشناخته برای آزمایش، مانند بارگیری داده‌ها از پایگاه داده یا نمایش انیمیشن‌های بی‌نهایت، ممکن است مشکلات همچنان پیش بیاید.

نمودار جریان حلقه ای را نشان می دهد که بررسی می کند که آیا برنامه قبل از قبولی آزمایشی غیرفعال است یا خیر
شکل 1 : همگام سازی تست.

برای افزایش قابلیت اطمینان مجموعه آزمایشی خود، می‌توانید راهی برای ردیابی عملیات پس‌زمینه نصب کنید، مانند Espresso Idling Resources . همچنین، می‌توانید ماژول‌هایی را برای نسخه‌های آزمایشی جایگزین کنید که می‌توانید برای بی‌کاری جستجو کنید یا همگام‌سازی را بهبود می‌بخشند، مانند TestDispatcher برای کوروتین‌ها یا RxIdler برای RxJava.

نمودار نشان دهنده شکست تست زمانی که همگام سازی بر اساس انتظار برای یک زمان ثابت است
شکل 2 : استفاده از خواب در تست ها منجر به آهسته یا پوسته پوسته شدن تست ها می شود.

راه های بهبود ثبات

تست‌های بزرگ می‌توانند رگرسیون‌های زیادی را در یک زمان ثبت کنند، زیرا چندین مؤلفه یک برنامه را آزمایش می‌کنند. آنها معمولاً روی شبیه‌سازها یا دستگاه‌ها اجرا می‌شوند، به این معنی که وفاداری بالایی دارند. در حالی که تست های بزرگ سرتاسر پوشش جامعی را ارائه می دهند، اما بیشتر مستعد خرابی های گاه به گاه هستند.

اقدامات اولیه ای که می توانید برای کاهش پوسته پوسته شدن انجام دهید موارد زیر است:

  • دستگاه ها را به درستی پیکربندی کنید
  • جلوگیری از مشکلات همگام سازی
  • اجرای مجدد تلاش ها

برای ایجاد تست‌های بزرگ با استفاده از Compose یا Espresso ، معمولاً یکی از فعالیت‌های خود را شروع می‌کنید و همانطور که یک کاربر انجام می‌دهد پیمایش می‌کنید و تأیید می‌کنید که رابط کاربری با استفاده از ادعاها یا آزمایش‌های اسکرین شات به درستی رفتار می‌کند.

سایر چارچوب‌ها، مانند UI Automator ، دامنه وسیع‌تری را در اختیار شما قرار می‌دهند، زیرا می‌توانید با رابط کاربری سیستم و سایر برنامه‌ها تعامل داشته باشید. با این حال، تست‌های UI Automator ممکن است نیاز به همگام‌سازی دستی بیشتری داشته باشند، بنابراین تمایل کمتری دارند.

پیکربندی دستگاه ها

ابتدا، برای بهبود قابلیت اطمینان تست های خود، باید مطمئن شوید که سیستم عامل دستگاه به طور غیرمنتظره ای در اجرای تست ها اختلال ایجاد نمی کند. به عنوان مثال، هنگامی که یک گفتگوی به روز رسانی سیستم در بالای برنامه های دیگر نشان داده می شود یا زمانی که فضای روی دیسک کافی نیست.

ارائه دهندگان مزرعه دستگاه ها دستگاه ها و شبیه سازهای خود را پیکربندی می کنند تا به طور معمول نیازی به انجام هیچ اقدامی نباشید. با این حال، آنها ممکن است دستورالعمل های پیکربندی خود را برای موارد خاص داشته باشند.

دستگاه های مدیریت شده توسط Gradle

اگر خودتان شبیه‌سازها را مدیریت می‌کنید، می‌توانید از دستگاه‌های مدیریت‌شده Gradle برای تعیین اینکه از چه دستگاه‌هایی برای اجرای آزمایش‌های خود استفاده کنید استفاده کنید:

android {
  testOptions {
    managedDevices {
      localDevices {
        create("pixel2api30") {
          // Use device profiles you typically see in Android Studio.
          device = "Pixel 2"
          // Use only API levels 27 and higher.
          apiLevel = 30
          // To include Google services, use "google".
          systemImageSource = "aosp"
        }
      }
    }
  }
}

با این پیکربندی، دستور زیر یک تصویر شبیه ساز ایجاد می کند، یک نمونه را شروع می کند، تست ها را اجرا می کند و آن را خاموش می کند.

./gradlew pixel2api30DebugAndroidTest

دستگاه‌های مدیریت‌شده Gradle دارای مکانیسم‌هایی برای امتحان مجدد در صورت قطع شدن دستگاه و سایر بهبودها هستند.

جلوگیری از مشکلات همگام سازی

مؤلفه‌هایی که عملیات پس‌زمینه یا ناهمزمان را انجام می‌دهند، می‌توانند منجر به شکست تست شوند، زیرا یک دستور آزمایشی قبل از آماده شدن رابط کاربری برای آن اجرا شده است. با افزایش دامنه آزمایش، احتمال پوسته پوسته شدن افزایش می یابد. این مسائل همگام‌سازی منبع اصلی پوسته پوسته شدن هستند، زیرا چارچوب‌های آزمایشی باید استنباط کنند که آیا یک فعالیت در حال بارگذاری انجام می‌شود یا اینکه باید بیشتر منتظر بماند.

راه حل ها

می‌توانید از منابع بی‌حرکت اسپرسو برای نشان دادن زمانی که یک برنامه مشغول است استفاده کنید، اما ردیابی هر عملیات ناهمزمان، به‌ویژه در آزمایش‌های انتها به انتها بسیار دشوار است. همچنین، نصب منابع بی‌حرکت بدون آلوده کردن کد تحت آزمایش، دشوار است.

به جای تخمین زدن مشغول بودن یا نبودن یک فعالیت، می‌توانید آزمایش‌های خود را تا زمانی که شرایط خاصی برآورده شود، صبر کنید. برای مثال، می‌توانید منتظر بمانید تا متن یا مؤلفه خاصی در UI نشان داده شود.

Compose مجموعه ای از API های آزمایشی را به عنوان بخشی از ComposeTestRule دارد تا منتظر تطبیق های مختلف باشد:

fun waitUntilAtLeastOneExists(matcher: SemanticsMatcher, timeout: Long = 1000L)

fun waitUntilDoesNotExist(matcher: SemanticsMatcher, timeout: Long = 1000L)

fun waitUntilExactlyOneExists(matcher: SemanticsMatcher,  timeout: Long = 1000L)

fun waitUntilNodeCount(matcher: SemanticsMatcher, count: Int, timeout: Long = 1000L)

و یک API عمومی که هر تابعی را که یک Boolean برمی گرداند را می گیرد:

fun waitUntil(timeoutMillis: Long, condition: () -> Boolean): Unit

مثال استفاده:

composeTestRule.waitUntilExactlyOneExists(hasText("Continue")</code>)</p></td>

مکانیسم ها را دوباره امتحان کنید

شما باید تست های پوسته پوسته را برطرف کنید، اما گاهی اوقات شرایطی که باعث شکست آنها می شود آنقدر غیرمحتمل است که تولید مجدد آنها دشوار است. در حالی که همیشه باید تست های پوسته پوسته را پیگیری کرده و آن ها را برطرف کنید، یک مکانیسم امتحان مجدد می تواند با اجرای چندین بار تست تا زمانی که موفق شود، به حفظ بهره وری توسعه دهنده کمک کند.

برای جلوگیری از مشکلاتی مانند:

  • زمان اتصال به دستگاه تمام شد یا اتصال قطع شد
  • شکست تک آزمون

نصب یا پیکربندی تلاش های مجدد به چارچوب های آزمایشی و زیرساخت شما بستگی دارد، اما مکانیسم های معمولی عبارتند از:

  • یک قانون JUnit که هر آزمایشی را چندین بار تکرار می کند
  • یک اقدام یا مرحله مجدد در گردش کار CI شما
  • سیستمی برای راه اندازی مجدد شبیه ساز در صورت عدم پاسخگویی، مانند دستگاه های مدیریت شده توسط Gradle.