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

כשמתכננים את אסטרטגיית הבדיקה של רכיב או מערכת, יש היבטים קשורים של בדיקה:

  • היקף: באיזה חלק מהקוד נשלחת הבדיקה? במסגרת הבדיקות אפשר לאמת ה-method batch, את כל האפליקציה או במקום כלשהו באמצע. ההיקף שנבדק נמצא בבדיקה, ומתייחס אליו בדרך כלל בתור הנושא שמתחת בדוק את, אבל גם את המערכת בבדיקה או את היחידה בבדיקה.
  • מהירות: באיזו מהירות מתבצעת הבדיקה? מהירויות הבדיקה יכולות להיות שונות מאלפיות שנייה תוך כמה דקות.
  • Fidelity: How "real-world" היא הבדיקה? לדוגמה, אם חלק מהקוד שבודקים צריך לשלוח בקשת רשת, האם קוד הבדיקה בבקשת הרשת הזו, או שהיא מזיפת את התוצאה? אם הבדיקה בפועל מדברת לרשת, פירוש הדבר הוא שרמת הדיוק שלו גבוהה יותר. הפשרה היא הבדיקה עשויה להימשך זמן רב יותר, לגרום לשגיאות אם הרשת מושבתת או עלול להיות יקר.

כדאי לקרוא מה כדאי לבדוק כדי ללמוד איך להתחיל להגדיר את אסטרטגיית הבדיקה.

בידוד ויחסי תלות

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

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

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

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

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

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

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

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

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

דוגמה: איך מוודאים שהקריאה ל-method במסד הנתונים בוצעה בדיוק פעם אחת.

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

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

מרגל 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: בדיקה גדולה שמכסה את רוב האפליקציה ומזייף נתונים מרחוק.

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

רובולקטרי

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

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