שימוש במכפילים לבדיקה ב-Android

כשבודקים רכיב או מערכת של רכיבים, עושים זאת בבידוד. לדוגמה, כדי לבדוק ViewModel, לא צריך להפעיל אמולטור ולהפעיל ממשק משתמש כי הוא לא תלוי (או לא אמור להיות תלוי) במסגרת Android.

עם זאת, יכול להיות שהנושא שנבדק תלוי בגורמים אחרים כדי לפעול. לדוגמה, יכול להיות ש-ViewModel תהיה תלויה במאגר נתונים כדי לפעול.

כשצריך לספק תלות לנושא הבדיקה, שיטה נפוצה היא ליצור אובייקט כפול לבדיקה (או אובייקט בדיקה). עותקים כפולים לבדיקה הם אובייקטים שנראים ומתנהגים כמו רכיבים באפליקציה, אבל הם נוצרים בבדיקה כדי לספק התנהגות או נתונים ספציפיים. היתרונות העיקריים הם שהם הופכים את הבדיקות מהירות ופשוטות יותר.

סוגי דמויי-בדיקה

יש כל מיני סוגים של מכפילים של בדיקה:

מזויף עותק כפול לבדיקה עם הטמעה 'פועלת' של הכיתה, אבל היא מוטמעת בצורה שמתאימה לבדיקות אבל לא מתאימה לסביבת הייצור.

דוגמה: מסד נתונים בזיכרון.

מוצרים מזויפים לא דורשים מסגרת לועגת, והם קלים. הן מומלצות.

הדמיה ניסוי כפול מתנהג לפי האופן שבו אתם מתכנתים אותו, ושיש לו ציפיות לגבי האינטראקציות שלו. בדיקות של מודלים מדומים ייכשל אם האינטראקציות שלהם לא תואמות לדרישות שהגדרתם. כדי להשיג את כל זה, בדרך כלל יוצרים דוגמאות באמצעות מסגרת משעשעת.

דוגמה: אימות ששיטה במסד נתונים הוזמנה בדיוק פעם אחת.

Stub עותק בדיקה שמתנהג כפי שתכננתם אותו, אבל אין לו ציפיות לגבי האינטראקציות שלו. בדרך כלל נוצרים באמצעות מסגרת של בדיקת מודלים. עדיף להשתמש ב-Fakes במקום ב-stubs, מטעמי פשטות.
dummy ערך כפול של בדיקה שמועבר אבל לא בשימוש, למשל אם אתם צריכים רק לספק אותו כפרמטר.

דוגמה: פונקציה ריקה שהועברה כקריאה חוזרת (callback) של קליק.

מרגל wrapper מעל אובייקט אמיתי שעוקב גם אחרי מידע נוסף מסוים, בדומה לחיקויים. בדרך כלל, מומלץ להימנע מהם כדי לא להוסיף מורכבות. לכן, מומלץ להשתמש במודלים מזויפים או במודלים מדומים במקום במודלים של ריגול.
צל זיוף שנעשה בו שימוש ב-Robolectric.

דוגמה לשימוש בזיוף

נניח שאתם רוצים לבצע בדיקת יחידה של ViewModel שמבוסס על ממשק שנקרא UserRepository וחושף את שם המשתמש הראשון לממשק המשתמש. כדי ליצור בדיקת כפולה מזויפת, אפשר להטמיע את הממשק והחזרת הנתונים הידועים.

object FakeUserRepository : UserRepository {
    fun getUsers() = listOf(UserAlice, UserBob)
}

val const UserAlice = User("Alice")
val const UserBob = User("Bob")

UserRepository המזויף לא צריך להיות תלוי במקורות הנתונים המקומיים והמרוחקים שבהם גרסת הייצור תשתמש. הקובץ נמצא בקבוצת מקורות הבדיקה ולא יישלח עם אפליקציית הייצור.

תלות מזויפת יכולה להחזיר נתונים ידועים בלי להסתמך על מקורות נתונים מרוחקים
איור 1: תלות מזויפת בבדיקת יחידה.

הבדיקה הבאה מוודאת ש-ViewModel חושף בצורה נכונה את שם המשתמש הראשון לתצוגה.

@Test
fun viewModelA_loadsUsers_showsFirstUser() {
    // Given a VM using fake data
    val viewModel = ViewModelA(FakeUserRepository) // Kicks off data load on init

    // Verify that the exposed data is correct
    assertEquals(viewModel.firstUserName, UserAlice.name)
}

בבדיקת יחידה אפשר להחליף בקלות את UserRepository בזיוף, כי ה-ViewModel נוצר על ידי הבודק. עם זאת, יכול להיות מאתגר להחליף רכיבים שרירותיים בבדיקות גדולות יותר.

החלפת רכיבים והחדרת תלות

כשאין לבדיקות שליטה על יצירת המערכות שנבדקות, החלפת רכיבים ברכיבי בדיקה כפולים הופכת למורכבת יותר, ודורשת שהארכיטקטורה של האפליקציה תעמוד בדרישות של עיצוב ניתן לבדיקה.

גם בדיקות גדולות מקצה לקצה יכולות להפיק תועלת מהשימוש במכפילים של בדיקה, כמו בדיקה אינסטרומנטלית של ממשק משתמש שמנווטת בתהליך מלא של המשתמש באפליקציה. במקרה כזה, כדאי שהבדיקה תהיה הרמטית. בדיקה הרמטית מונעת יחסי תלות חיצוניים, כמו אחזור נתונים מהאינטרנט. כך אפשר לשפר את האמינות והביצועים.

איור 2: בדיקה גדולה שמכסה את רוב האפליקציה ומזייף נתונים מרחוק.

אפשר לעצב את האפליקציה כך שתשיג את הגמישות הזו באופן ידני, אבל מומלץ להשתמש ב-framework של החדרת תלות כמו Hilt כדי להחליף רכיבים באפליקציה בזמן הבדיקה. מדריך הבדיקה של Hilt

השלבים הבאים

בדף שיטות בדיקה מוסבר איך אפשר לשפר את הפרודוקטיביות באמצעות סוגים שונים של בדיקות.