יסודות הבדיקה

מעבדת קוד זו היא חלק מהקורס המתקדם של Android בקורס Kotlin. כדי להפיק את המקסימום מהקורס הזה, אם עובדים על מעבדות הקוד ברצף, לא חובה לעשות זאת. כל שיעורי הקוד של הקורסים מופיעים בדף הנחיתה המתקדם של Android ב-Kotlin codelabs.

מבוא

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

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

בסדרה זו של מעבדות קוד, תלמדו איך ליצור אוסף בדיקות (חבילה לבדיקה) עבור אפליקציה בעולם האמיתי.

מעבדת הקוד הראשונה הזו מכסה את העקרונות הבסיסיים של הבדיקה ב-Android, וכותבים את הבדיקות הראשונות ולומדים איך לבדוק את LiveData ואת ViewModel.

דברים שחשוב לדעת

כדאי שתכירו את:

מה תלמדו

תוכל ללמוד על הנושאים הבאים:

  • איך לכתוב ולהריץ בדיקות יחידה ב-Android
  • איך משתמשים בפיתוח מבוסס-בדיקה
  • איך לבחור בדיקות אינסטרומנטציה ובדיקות מקומיות

תלמדו על הספריות ועקרונות הקוד הבאים:

הפעולות שתבצעו:

  • הגדרה, הפעלה ופרשנות של בדיקות מקומיות ואינסטרומנטציות ב-Android.
  • כתיבת בדיקות יחידה ב-Android באמצעות JUNIT4 ו-Hamcrest.
  • כתיבה של בדיקות פשוטות ב-LiveData וב-ViewModel.

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

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

כדי להתחיל, יש להוריד את הקוד:

להורדת קובץ Zip

לחלופין, אפשר לשכפל את מאגר GitHub לקוד:

$ git clone https://github.com/googlecodelabs/android-testing.git
$ cd android-testing
$ git checkout starter_code

במשימה הזו צריך להריץ את האפליקציה ולגלות את בסיס הקוד.

שלב 1: מריצים את האפליקציה לדוגמה

אחרי שמורידים את האפליקציה 'רשימת משימות', פותחים אותה ב-Android Studio ומפעילים אותה. הוא אמור להידור. כדי לבחון את האפליקציה, מבצעים את הפעולות הבאות:

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

שלב 2: עיון בקוד האפליקציה לדוגמה

אפליקציית TO-DO מבוססת על דגימת הבדיקה הפופולרית של ארכיטקטורת ארכיטקטורה (באמצעות גרסת הארכיטקטורה רספונסיבית של הדוגמה). האפליקציה בנויה לפי הארכיטקטורה במדריך לארכיטקטורת אפליקציות. המודל משתמש ב-ViewModel עם Fragments, מאגר וחדר. אם אתם מכירים את אחת מהדוגמאות הבאות, לאפליקציה הזו יש ארכיטקטורה דומה:

חשוב יותר להבין את הארכיטקטורה הכללית של האפליקציה מאשר להבין לעומק את הלוגיקה בכל שכבה.

זהו סיכום החבילות שתמצא:

חבילה: com.example.android.architecture.blueprints.todoapp

.addedittask

הוספה או עריכה של מסך משימה: קוד שכבה של ממשק משתמש להוספה או לעריכה של משימה.

.data

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

.statistics

מסך הנתונים הסטטיסטיים: קוד שכבה של ממשק משתמש למסך הנתונים הסטטיסטיים.

.taskdetail

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

.tasks

מסך המשימות: קוד שכבת ממשק המשתמש עבור הרשימה של כל המשימות.

.util

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

שכבת נתונים (.data)

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

הקואורדינטות של DefaultTasksRepository מתווכות או מתווכות בין שכבת הרשת לבין שכבת מסד הנתונים, והיא זו שמחזירה נתונים לשכבת ממשק המשתמש.

שכבת ממשק משתמש ( .addedittask, .statistics, .taskdetail, .tasks)

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

ניווט

הניווט באפליקציה נשלט על ידי רכיב הניווט. הוא מוגדר בקובץ nav_graph.xml. הניווט מופעל במודלים של תצוגות מפורטות באמצעות הכיתה Event; המודלים של התצוגה קובעים גם אילו ארגומנטים יעברו. המקטעים מעיינים ב-Event ומבצעים את הניווט בפועל בין מסכים.

במשימה הזו, מריצים את הבדיקות הראשונות.

  1. ב-Android Studio, פותחים את החלונית Project (פרויקט) ומוצאים את שלוש התיקיות הבאות:
  • com.example.android.architecture.blueprints.todoapp
  • com.example.android.architecture.blueprints.todoapp (androidTest)
  • com.example.android.architecture.blueprints.todoapp (test)

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

  • main: מכיל את קוד האפליקציה שלכם. הקוד הזה משותף לכל הגרסאות השונות של האפליקציה שאפשר ליצור (שנקרא גרסאות Build)
  • androidTest: מכיל בדיקות המוכרות כאינסטרומנטציות.
  • test: מכילה בדיקות שנקראות בדיקות מקומיות.

ההבדל בין בדיקות מקומיות לבין בדיקות אינסטרומנטליות הוא הדרך שבה הן מתבצעות.

בדיקות מקומיות (test קבוצת המקור)

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

בבדיקות מקומיות של Android Studio מיוצגים סמל של משולש ירוק ואדום.

בדיקות מתוכננות (androidTest קבוצת המקור)

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

ב-Android Studio, אינסטרומנטציות שהן מיוצגות מיוצגות על ידי Android עם סמל משולש בצבעי ירוק ואדום.

שלב 1: מפעילים בדיקה מקומית

  1. פותחים את התיקייה test עד שמופיע הקובץ ExampleUnitTest.kt.
  2. לוחצים עליה לחיצה ימנית ובוחרים באפשרות Run ExampleUnitTest.

הפלט הבא אמור להופיע בחלון Run בתחתית המסך:

  1. שימו לב לנקודות הסימון הירוקות והרחיבו את תוצאות הבדיקה כדי לוודא שבדיקה אחת בשם addition_isCorrect עברה. אנחנו יודעים שתוספת זו פועלת כמצופה!

שלב 2: ביצוע הבדיקה נכשל

זו הבדיקה שהרצתם עכשיו.

ExampleItemTest.kt

// A test class is just a normal class
class ExampleUnitTest {

   // Each test is annotated with @Test (this is a Junit annotation)
   @Test
   fun addition_isCorrect() {
       // Here you are checking that 4 is the same as 2+2
       assertEquals(4, 2 + 2)
   }
}

חשוב לשים לב שבדיקות

  • הם כיתה באחת מהקבוצות של מקורות הבדיקה.
  • מכילים פונקציות שמתחילות בהערה @Test (כל פונקציה היא בדיקה יחידה).
  • מכילים באופן אוטומטי הצהרות בעלות.

Android משתמש בספריית הבדיקה JUNIT לבדיקה (בקוד זה LabJ4). שתי הטענות וההערה @Test מגיעות מ-JUNIT.

בדיקה היא הבסיס לבדיקה. זוהי הצהרת קוד שבודקת שהקוד או האפליקציה התנהגו כצפוי. במקרה זה, הקביעה היא assertEquals(4, 2 + 2) שבודקת ש-4 שווה ל-2 + 2.

כדי לראות איך נראית בדיקה שנכשלה, יש להוסיף הצהרה שאפשר לראות בקלות. צריך לבדוק ש-3 הוא 1+1.

  1. עליך להוסיף את assertEquals(3, 1 + 1) לבדיקה של addition_isCorrect.

ExampleItemTest.kt

class ExampleUnitTest {

   // Each test is annotated with @Test (this is a Junit annotation)
   @Test
   fun addition_isCorrect() {
       assertEquals(4, 2 + 2)
       assertEquals(3, 1 + 1) // This should fail
   }
}
  1. מריצים את הבדיקה.
  1. בתוצאות הבדיקה, מסמנים את ה-X ליד הבדיקה.

  1. כמו כן:
  • טענה אחת שנכשלה נכשלה בכל הבדיקה.
  • אתם מקבלים את הערך הצפוי (3) לעומת הערך שחושב בפועל (2).
  • הופנתה אל שורת הטענה שנכשלה (ExampleUnitTest.kt:16).

שלב 3: מפעילים בדיקה של אינסטרומנטציה

בדיקות שעברו אינסטרומנטציה נמצאות בקבוצת המקורות של androidTest.

  1. פותחים את קבוצת המקור של androidTest.
  2. מריצים את הבדיקה שנקראת ExampleInstrumentedTest.

ExampleSampleedTest

@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
    @Test
    fun useAppContext() {
        // Context of the app under test.
        val appContext = InstrumentationRegistry.getInstrumentation().targetContext
        assertEquals("com.example.android.architecture.blueprints.reactive",
            appContext.packageName)
    }
}

בשונה מהבדיקה המקומית, הבדיקה הזו פועלת במכשיר (בדוגמה שמתחת לטלפון אמולציה של Pixel 2):

אם יש לך מכשיר מחובר או אמולטור פועל, הבדיקה אמורה להופיע על האמולטור.

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

שלב 1: יצירת כיתת בדיקה

  1. בקבוצת המקור של main, ב-todoapp.statistics, פותחים את StatisticsUtils.kt.
  2. מוצאים את הפונקציה getActiveAndCompletedStats.

StatistsUillas.kt

internal fun getActiveAndCompletedStats(tasks: List<Task>?): StatsResult {

   val totalTasks = tasks!!.size
   val numberOfActiveTasks = tasks.count { it.isActive }
   val activePercent = 100 * numberOfActiveTasks / totalTasks
   val completePercent = 100 * (totalTasks - numberOfActiveTasks) / totalTasks

   return StatsResult(
       activeTasksPercent = activePercent.toFloat(),
       completedTasksPercent = completePercent.toFloat()
   )
  
}

data class StatsResult(val activeTasksPercent: Float, val completedTasksPercent: Float)

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

ב-Android Studio אפשר למצוא כלים ליצירת כלים נלווים לבדיקה עבור הפונקציה הזו.

  1. לוחצים לחיצה ימנית על getActiveAndCompletedStats ובוחרים באפשרות Generate > Test.

תיבת הדו-שיח Create Test תיפתח:

  1. משנים את שם הכיתה: ל-StatisticsUtilsTest (במקום StatisticsUtilsKtTest; עדיף שלא יהיה KT בשם הכיתה לבדיקה).
  2. משאירים את ברירות המחדל האחרות. ספריית יחידה 4 היא ספריית הבדיקה המתאימה. חבילת היעד נכונה (היא משקפת את המיקום של מחלקת StatisticsUtils) ואין צורך לסמן אף אחת מתיבות הסימון (פעולה זו יוצרת קוד נוסף, אך לכתוב את הבדיקה מאפס).
  3. מקישים על אישור

תיבת הדו-שיח בחירת ספריית יעד תיפתח:

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

  1. יש לבחור את הספרייה test (ולא androidTest) כי צריך לכתוב בדיקות מקומיות.
  2. לוחצים על אישור.
  3. שימו לב לכיתה StatisticsUtilsTest שנוצרה ב-test/statistics/.

שלב 2: כתיבה של פונקציית הבדיקה הראשונה

אתם כותבים בדיקה שבודקת את הדברים הבאים:

  • אם אין משימות שהושלמו ומשימה פעילה אחת,
  • שאחוז הבדיקות הפעילות הוא 100%,
  • ואחוז המשימות שבוצעו הוא 0%.
  1. פתיחת StatisticsUtilsTest
  2. צריך ליצור פונקציה בשם getActiveAndCompletedStats_noCompleted_returnsHundredZero.

StatistsUillasTest.kt

class StatisticsUtilsTest {

    fun getActiveAndCompletedStats_noCompleted_returnsHundredZero() {
        // Create an active task

        // Call your function

        // Check the result
    }
}
  1. מוסיפים את ההערה @Test מעל שם הפונקציה כדי לציין שמדובר בבדיקה.
  2. יצירה של רשימת משימות.
// Create an active task 
val tasks = listOf<Task>(
            Task("title", "desc", isCompleted = false)
        )
  1. התקשרות אל getActiveAndCompletedStats לביצוע המשימות האלה.
// Call your function
val result = getActiveAndCompletedStats(tasks)
  1. יש לוודא ש-result הוא מה שציפית לקבל, באמצעות טענות.
// Check the result
assertEquals(result.completedTasksPercent, 0f)
assertEquals(result.activeTasksPercent, 100f)

זה הקוד המלא.

StatistsUillasTest.kt

class StatisticsUtilsTest {

    @Test
    fun getActiveAndCompletedStats_noCompleted_returnsHundredZero() {

        // Create an active task (the false makes this active)
        val tasks = listOf<Task>(
            Task("title", "desc", isCompleted = false)
        )
        // Call your function
        val result = getActiveAndCompletedStats(tasks)

        // Check the result
        assertEquals(result.completedTasksPercent, 0f)
        assertEquals(result.activeTasksPercent, 100f)
    }
}
  1. מריצים את הבדיקה (לוחצים לחיצה ימנית על StatisticsUtilsTest ובוחרים באפשרות הפעלה).

עליה לעבור:

שלב 3: מוסיפים את התלות של המארקסט

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

assertEquals(result.completedTasksPercent, 0f)

// versus

assertThat(result.completedTasksPercent, `is`(0f))

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

  1. יש לפתוח את build.grade (Module: app) ולהוסיף את התלויות הבאות.

app/build.gradle

dependencies {
    // Other dependencies
    testImplementation "org.hamcrest:hamcrest-all:$hamcrestVersion"
}

בדרך כלל, נעשה שימוש ב-implementation כשמוסיפים תלות, אבל כן נעשה שימוש במאפיין testImplementation. כאשר אתם מוכנים לשתף את האפליקציה שלכם עם העולם, מומלץ לא להגדיל את כמות ה-APK באמצעות קוד הבדיקה או תלויים באפליקציה שלכם. אפשר לקבוע אם ספרייה תכלל בקוד הראשי או בקוד הבדיקה באמצעות הגדרות דרגה. ההגדרות הנפוצות ביותר הן:

  • implementation—התלות זמינה בכל קבוצות המקור, כולל קבוצות המקור של הבדיקה.
  • testImplementation – התלויה זמינה רק בקבוצת המקור של הבדיקה.
  • androidTestImplementation — התלות זמינה רק בקבוצת המקור ב-androidTest.

ההגדרה שבה משתמשים תלויה במקום שבו ניתן להשתמש. אם כותבים:

testImplementation "org.hamcrest:hamcrest-all:$hamcrestVersion"

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

שלב 4: כתיבת טענות באמצעות האמברסט

  1. יש לעדכן את הבדיקה של getActiveAndCompletedStats_noCompleted_returnsHundredZero() כדי להשתמש ב-assertThat של Humcrest במקום ב-assertEquals.
// REPLACE
assertEquals(result.completedTasksPercent, 0f)
assertEquals(result.activeTasksPercent, 100f)

// WITH
assertThat(result.activeTasksPercent, `is`(100f))
assertThat(result.completedTasksPercent, `is`(0f))

לתשומת ליבך, אפשר להשתמש בייבוא של import org.hamcrest.Matchers.`is` אם מתבקשים לעשות זאת.

הבדיקה הסופית נראית כמו הקוד הבא.

StatistsUillasTest.kt

import com.example.android.architecture.blueprints.todoapp.data.Task
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.`is`
import org.junit.Test

class StatisticsUtilsTest {

    @Test
    fun getActiveAndCompletedStats_noCompleted_returnsHundredZero {

        // Create an active tasks (the false makes this active)
        val tasks = listOf<Task>(
            Task("title", "desc", isCompleted = false)
        )
        // Call your function
        val result = getActiveAndCompletedStats(tasks)

        // Check the result
        assertThat(result.activeTasksPercent, `is`(100f))
        assertThat(result.completedTasksPercent, `is`(0f))

    }
}
  1. כדאי להריץ את הבדיקה המעודכנת כדי לוודא שהיא עדיין פועלת.

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

זוהי משימה אופציונלית שאפשר לתרגל.

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

שלב 1. כתיבת הבדיקות

כתיבת בדיקות למקרה שיש לכם רשימת משימות רגילה:

  1. אם יש משימה אחת שהושלמה ולא בוצעו משימות פעילות, האחוז באחוזים של activeTasks צריך להיות 0f ואחוז המשימות שבוצעו צריך להיות 100f .
  2. אם יש שתי משימות שבוצעו ושלוש משימות פעילות, האחוז צריך להיות 40f והאחוז הפעיל צריך להיות 60f.

שלב 2. כתיבת בדיקה לבאג

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

internal fun getActiveAndCompletedStats(tasks: List<Task>?): StatsResult {

   val totalTasks = tasks!!.size
   val numberOfActiveTasks = tasks.count { it.isActive }
   val activePercent = 100 * numberOfActiveTasks / totalTasks
   val completePercent = 100 * (totalTasks - numberOfActiveTasks) / totalTasks

   return StatsResult(
       activeTasksPercent = activePercent.toFloat(),
       completedTasksPercent = completePercent.toFloat()
   )
  
}

כדי לתקן את הקוד ולכתוב בדיקות, עליכם להשתמש בפיתוח מבוסס-בדיקות. פיתוח מבוסס-בדיקה פועל לפי השלבים הבאים.

  1. יש לכתוב את הבדיקה באמצעות המבנה לשם, מתי ואז, ולציין שם בהתאם למוסכמות.
  2. מוודאים שהבדיקה נכשלה.
  3. יש להזין את הקוד המינימלי כדי לעבור את הבדיקה.
  4. חוזרים על הפעולה עבור כל הבדיקות.

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

  1. אם יש רשימה ריקה (emptyList()), שני האחוזים צריכים להיות 0f.
  2. אם אירעה שגיאה בטעינת המשימות, הרשימה תהיה null והשניים צריכים להיות 0f.
  3. מריצים את הבדיקות ומוודאים שהם נכשלים:

שלב 3. תיקון הבאג

עכשיו, אחרי שהתבצעה הבדיקה, יש לתקן את הבאג.

  1. יש להחזיר את הבאג בכתובת getActiveAndCompletedStats על ידי החזרת הערך 0f אם הערך של tasks הוא null או ריק:
internal fun getActiveAndCompletedStats(tasks: List<Task>?): StatsResult {

    return if (tasks == null || tasks.isEmpty()) {
        StatsResult(0f, 0f)
    } else {
        val totalTasks = tasks.size
        val numberOfActiveTasks = tasks.count { it.isActive }
        StatsResult(
            activeTasksPercent = 100f * numberOfActiveTasks / tasks.size,
            completedTasksPercent = 100f * (totalTasks - numberOfActiveTasks) / tasks.size
        )
    }
}
  1. הפעילו שוב את הבדיקות וודאו שכל הבדיקות עברו בהצלחה.

על ידי המשך הטיפול ב-TDD וכתיבת הבדיקות, עזרת לוודא:

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

פתרון: כתיבת בדיקות נוספות

אלה כל הבדיקות וקוד התכונה המתאים.

StatistsUillasTest.kt

class StatisticsUtilsTest {

    @Test
    fun getActiveAndCompletedStats_noCompleted_returnsHundredZero {
        val tasks = listOf(
            Task("title", "desc", isCompleted = false)
        )
        // When the list of tasks is computed with an active task
        val result = getActiveAndCompletedStats(tasks)

        // Then the percentages are 100 and 0
        assertThat(result.activeTasksPercent, `is`(100f))
        assertThat(result.completedTasksPercent, `is`(0f))
    }

    @Test
    fun getActiveAndCompletedStats_noActive_returnsZeroHundred() {
        val tasks = listOf(
            Task("title", "desc", isCompleted = true)
        )
        // When the list of tasks is computed with a completed task
        val result = getActiveAndCompletedStats(tasks)

        // Then the percentages are 0 and 100
        assertThat(result.activeTasksPercent, `is`(0f))
        assertThat(result.completedTasksPercent, `is`(100f))
    }

    @Test
    fun getActiveAndCompletedStats_both_returnsFortySixty() {
        // Given 3 completed tasks and 2 active tasks
        val tasks = listOf(
            Task("title", "desc", isCompleted = true),
            Task("title", "desc", isCompleted = true),
            Task("title", "desc", isCompleted = true),
            Task("title", "desc", isCompleted = false),
            Task("title", "desc", isCompleted = false)
        )
        // When the list of tasks is computed
        val result = getActiveAndCompletedStats(tasks)

        // Then the result is 40-60
        assertThat(result.activeTasksPercent, `is`(40f))
        assertThat(result.completedTasksPercent, `is`(60f))
    }

    @Test
    fun getActiveAndCompletedStats_error_returnsZeros() {
        // When there's an error loading stats
        val result = getActiveAndCompletedStats(null)

        // Both active and completed tasks are 0
        assertThat(result.activeTasksPercent, `is`(0f))
        assertThat(result.completedTasksPercent, `is`(0f))
    }

    @Test
    fun getActiveAndCompletedStats_empty_returnsZeros() {
        // When there are no tasks
        val result = getActiveAndCompletedStats(emptyList())

        // Both active and completed tasks are 0
        assertThat(result.activeTasksPercent, `is`(0f))
        assertThat(result.completedTasksPercent, `is`(0f))
    }
}

StatistsUillas.kt

internal fun getActiveAndCompletedStats(tasks: List<Task>?): StatsResult {

    return if (tasks == null || tasks.isEmpty()) {
        StatsResult(0f, 0f)
    } else {
        val totalTasks = tasks.size
        val numberOfActiveTasks = tasks.count { it.isActive }
        StatsResult(
            activeTasksPercent = 100f * numberOfActiveTasks / tasks.size,
            completedTasksPercent = 100f * (totalTasks - numberOfActiveTasks) / tasks.size
        )
    }
}

עבודה טובה עם היסודות של כתיבה והפעלה של בדיקות! בשלב הבא עליך ללמוד איך לכתוב בדיקות בסיסיות של ViewModel ושל LiveData.

בשאר תכונות שיעור הקוד תלמדו איך לכתוב בדיקות לשתי כיתות נפוצות ב-Android, ברוב האפליקציות — ViewModel ו-LiveData.

אפשר להתחיל בכתיבת בדיקות עבור TasksViewModel.


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



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

TasksViewModel.kt

fun addNewTask() {
   _newTaskEvent.value = Event(Unit)
}

שלב 1. יצירת כיתה של TasksViewModelTest

מבצעים את אותם השלבים שביצעת ל-StatisticsUtilTest בשלב הזה ויוצרים קובץ בדיקה עבור TasksViewModelTest.

  1. פותחים את הכיתה שרוצים לבדוק בחבילה tasks, TasksViewModel..
  2. לוחצים לחיצה ימנית על שם הכיתה TasksViewModel – > Generate -> Test.

  1. במסך יצירת בדיקה, לוחצים על אישור כדי לאשר (אין צורך לשנות את הגדרות ברירת המחדל).
  2. בתיבת הדו-שיח Choose Directory Directory, בוחרים את ספריית הבדיקה.

שלב 2. מתחילים לכתוב את בדיקת ה-ViewModel

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

  1. יצירת בדיקה חדשה בשם addNewTask_setsNewTaskEvent

TasksViewModelTest.kt

class TasksViewModelTest {

    @Test
    fun addNewTask_setsNewTaskEvent() {

        // Given a fresh TasksViewModel


        // When adding a new task


        // Then the new task event is triggered

    }
    
}

מה לגבי הקשר לאפליקציה?

כשיוצרים מופע של TasksViewModel לבדיקה, ה-constructor שלו דורש הקשר של אפליקציה. אבל בבדיקה הזו, אתם לא יוצרים אפליקציה מלאה עם פעילויות וממשק משתמש ועם מקטעים, אז איך מקבלים הקשר של אפליקציה?

TasksViewModelTest.kt

// Given a fresh ViewModel
        val tasksViewModel = TasksViewModel(???)

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

  1. הוספת הליבה של AndroidX Testing והתלויות החיצוניות
  2. הוספת תלות בספריית הבדיקות של Robolectric
  3. הוספת הערה לכיתה באמצעות הרצת הבדיקה של AndroidJunit4
  4. כתיבת קוד בדיקה של AndroidX

אתם עומדים לבצע את השלבים האלה, ואז להבין מה הם עושים ביחד.

שלב 3. הוספת תלויות הציונים

  1. אפשר להעתיק את התלות האלה בקובץ build.gradle של מודול האפליקציה & כדי להוסיף את הליבה הליבה של AndroidX והתלויות, וכן את התלות של בדיקות ה-Robolectric.

app/build.gradle

    // AndroidX Test - JVM testing
testImplementation "androidx.test.ext:junit-ktx:$androidXTestExtKotlinRunnerVersion"

    testImplementation "androidx.test:core-ktx:$androidXTestCoreVersion"

 testImplementation "org.robolectric:robolectric:$robolectricVersion"

שלב 4. הוספת מריץ בדיקה של JUNIT

  1. יש להוסיף את @RunWith(AndroidJUnit4::class) מעל לכיתת הבדיקה.

TasksViewModelTest.kt

@RunWith(AndroidJUnit4::class)
class TasksViewModelTest {
    // Test code
}

שלב 5. שימוש בבדיקת AndroidX

בשלב הזה אפשר להשתמש בספריית הבדיקה של AndroidX. כולל השיטה ApplicationProvider.getApplicationContext, שמקבלת הקשר לאפליקציה.

  1. אפשר ליצור TasksViewModel באמצעות ApplicationProvider.getApplicationContext() מספריית הבדיקה של AndroidX.

TasksViewModelTest.kt

// Given a fresh ViewModel
val tasksViewModel = TasksViewModel(ApplicationProvider.getApplicationContext())
  1. התקשרות אל addNewTask בtasksViewModel.

TasksViewModelTest.kt

tasksViewModel.addNewTask()

בשלב זה, הבדיקה שלך אמורה להיראות כמו הקוד הבא.

TasksViewModelTest.kt

    @Test
    fun addNewTask_setsNewTaskEvent() {

        // Given a fresh ViewModel
        val tasksViewModel = TasksViewModel(ApplicationProvider.getApplicationContext())

        // When adding a new task
        tasksViewModel.addNewTask()

        // Then the new task event is triggered
        // TODO test LiveData
    }
  1. מריצים את הבדיקה כדי לוודא שהיא פועלת.

קונספט: איך פועל בדיקת XX ב-Android?

מהי בדיקת XX?

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

ApplicationProvider.getApplicationContext()

אחד היתרונות של ה-APIs של בדיקת AndroidX הוא שהם מיועדים לעבוד גם בבדיקות מקומיות וגם בבדיקות אינסטרומנטליות. זה נהדר כי:

  • אפשר להריץ את אותה בדיקה כמו בדיקה מקומית או בדיקת אינסטרומנטציה.
  • אין צורך ללמוד ממשקי API שונים לבדיקות של בדיקות מקומיות לעומת בדיקות אינסטרומנטליות.

לדוגמה, כי כתבת את הקוד באמצעות ספריות בדיקה של AndroidX, אפשר להעביר את הכיתה TasksViewModelTest מהתיקייה test לתיקייה androidTest, והבדיקות עדיין יפעלו. getApplicationContext() פועל באופן שונה מעט אם הוא פועל כבדיקה מקומית או כאינסטרומנטציה:

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

מה זה Robolectric?

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

מה יקרה @RunWith(AndroidJUnit4::class)?

הפעלת בדיקה היא רכיב Junit שמפעיל בדיקות. ללא ראנר בדיקה, הבדיקות שלכם לא יפעלו. יש הרצת ברירת מחדל לבדיקה ש-JUNIT מקבל באופן אוטומטי. @RunWith תחליף את הרצת הבדיקה המוגדרת כברירת מחדל.

הרצת הבדיקה AndroidJUnit4 מאפשרת ל-AndroidX Testing להריץ את הבדיקה בצורה שונה, בהתאם לסוג הבדיקות: אינסטרומנטציה או בדיקות מקומיות.

שלב 6. תיקון אזהרות Robolectric

כשאתם מריצים את הקוד, שימו לב שנעשה שימוש ב-Robolectric.

בזכות בדיקת ה-AndroidX והרצפת הבדיקה של AndroidJunit4, אנחנו עושים זאת בלי לכתוב ישירות שורה אחת של קוד Robolectric!

ייתכן שיופיעו שתי אזהרות.

  • No such manifest file: ./AndroidManifest.xml
  • "WARN: Android SDK 29 requires Java 9..."

אפשר לעדכן את אזהרת No such manifest file: ./AndroidManifest.xml כדי לעדכן את קובץ הציונים.

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

app/build.gradle

    // Always show the result of every unit test when running via command line, even if it passes.
    testOptions.unitTests {
        includeAndroidResources = true

        // ... 
    }

האזהרה "WARN: Android SDK 29 requires Java 9..." מורכבת יותר. כדי לבצע בדיקות ב-Android Q יש צורך ב-Java 9. במקום לנסות להגדיר את Android להשתמש ב-Java 9, ב-codelab זה צריך לשמור על היעד ולהדר את SDK ב-28.

לסיכום:

  • לרוב, בדיקות של מודל תצוגה מפורטת יכולות להתבצע בקבוצת המקור test, כי הקוד שלהן לא נדרש בדרך כלל עם Android.
  • אפשר להשתמש בבדיקת AndroidX הספרייה כדי לקבל גרסאות בדיקה של רכיבים כמו אפליקציות ופעילויות.
  • כדי להפעיל סימולציה של קוד Android בקבוצת המקורות של test, אפשר להוסיף את התלות ב-Robolectric ואת ההערה עם @RunWith(AndroidJUnit4::class).

מזל טוב, אתם משתמשים בספריית הבדיקה של AndroidX וגם ב-Robolectric כדי להריץ בדיקה. הבדיקה שלך לא הסתיימה (עדיין לא כתבת הצהרה בבית, אבל היא אומרת רק // TODO test LiveData). מעכשיו עליך לכתוב הצהרות עם LiveData.

במשימה הזו מוסבר איך להצהיר על ערך LiveData נכון.

רוצה להמשיך בלי בדיקת מודל של addNewTask_setsNewTaskEvent כדי לראות מהמקום שבו הפסקת?

TasksViewModelTest.kt

    @Test
    fun addNewTask_setsNewTaskEvent() {

        // Given a fresh ViewModel
        val tasksViewModel = TasksViewModel(ApplicationProvider.getApplicationContext())

        // When adding a new task
        tasksViewModel.addNewTask()

        // Then the new task event is triggered
        // TODO test LiveData
    }
    

כדי לבדוק את LiveData מומלץ לבצע שתי פעולות:

  1. שימוש באפליקציה InstantTaskExecutorRule
  2. יש לוודא שתצפית אחת (LiveData)

שלב 1. שימוש ב- InstantTaskExecutorRule

InstantTaskExecutorRule הוא כלל JUNIT. אם מזינים את ההערה הזו עם ההערה @get:Rule, היא גורמת להפעלת קוד כלשהו בכיתה InstantTaskExecutorRule לפני ואחרי הבדיקות (כדי לראות את הקוד המדויק, אפשר להשתמש במקש הקיצור Command+B כדי להציג את הקובץ).

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

  1. מוסיפים את התלות בהיררכיה של ספריית הליבה של רכיבי הארכיטקטורה (שכוללת את הכלל הזה).

app/build.gradle

testImplementation "androidx.arch.core:core-testing:$archTestingVersion"
  1. פתיחה של TasksViewModelTest.kt
  2. הוספה של InstantTaskExecutorRule בתוך המחלקה TasksViewModelTest.

TasksViewModelTest.kt

class TasksViewModelTest {
    @get:Rule
    var instantExecutorRule = InstantTaskExecutorRule()
    
    // Other code...
}

שלב 2. הוספת ה-LiveDataTestUilla.kt Class

השלב הבא הוא לוודא שהבדיקה של LiveData מתבצעת.

כשאתם משתמשים ב-LiveData, בדרך כלל יש לכם פעילות או מקטע (LifecycleOwner) שצופים ב-LiveData.

viewModel.resultLiveData.observe(fragment, Observer {
    // Observer code here
})

התצפית הזו חשובה. נדרשים צופים פעילים ב-LiveData כדי

  • להפעיל אירועים מסוג onChanged.
  • מפעילים כל המרה.

כדי לקבל את ההתנהגות LiveData הצפויה עבור מודל התצוגה המפורטת 'LiveData', עליך לצפות ב-LiveData עם LifecycleOwner.

פעולה זו יוצרת בעיה: בבדיקה של TasksViewModel אין לך פעילות או שבר שתצפית על LiveData שלך. כדי לעקוף את הבעיה, אפשר להשתמש בשיטה observeForever, שבעזרתה אפשר לעקוב באופן קבוע אחרי LiveData, בלי צורך ב-LifecycleOwner. כשאתם observeForever, חשוב לזכור להסיר את הצופה או להסתכן בדליפת צופים.

זה נראה כמו הקוד הבא. בדיקה של:

@Test
fun addNewTask_setsNewTaskEvent() {

    // Given a fresh ViewModel
    val tasksViewModel = TasksViewModel(ApplicationProvider.getApplicationContext())


    // Create observer - no need for it to do anything!
    val observer = Observer<Event<Unit>> {}
    try {

        // Observe the LiveData forever
        tasksViewModel.newTaskEvent.observeForever(observer)

        // When adding a new task
        tasksViewModel.addNewTask()

        // Then the new task event is triggered
        val value = tasksViewModel.newTaskEvent.value
        assertThat(value?.getContentIfNotHandled(), (not(nullValue())))

    } finally {
        // Whatever happens, don't forget to remove the observer!
        tasksViewModel.newTaskEvent.removeObserver(observer)
    }
}

זה הרבה קוד בישול כדי להבחין בLiveData של בדיקה! יש כמה דרכים להיפטר מהבורה הזו. בחרת ליצור פונקציית תוסף בשם LiveDataTestUtil כדי להקל על הוספת צופים.

  1. יצירת קובץ Kotlin חדש בשם LiveDataTestUtil.kt בקבוצת המקור של test.


  1. מעתיקים את הקוד ומדביקים אותו למטה.

LiveDataTestUטרי.kt

import androidx.annotation.VisibleForTesting
import androidx.lifecycle.LiveData
import androidx.lifecycle.Observer
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
import java.util.concurrent.TimeoutException


@VisibleForTesting(otherwise = VisibleForTesting.NONE)
fun <T> LiveData<T>.getOrAwaitValue(
    time: Long = 2,
    timeUnit: TimeUnit = TimeUnit.SECONDS,
    afterObserve: () -> Unit = {}
): T {
    var data: T? = null
    val latch = CountDownLatch(1)
    val observer = object : Observer<T> {
        override fun onChanged(o: T?) {
            data = o
            latch.countDown()
            this@getOrAwaitValue.removeObserver(this)
        }
    }
    this.observeForever(observer)

    try {
        afterObserve.invoke()

        // Don't wait indefinitely if the LiveData is not set.
        if (!latch.await(time, timeUnit)) {
            throw TimeoutException("LiveData value was never set.")
        }

    } finally {
        this.removeObserver(observer)
    }

    @Suppress("UNCHECKED_CAST")
    return data as T
}

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

שלב 3. שימוש ב-getOrAPENDINGValue כדי לכתוב את הקביעה

בשלב זה, אתם משתמשים בשיטה getOrAwaitValue וכותבים הצהרה שמצהירה שהnewTaskEvent הופעל.

  1. קבלת הערך LiveData עבור newTaskEvent באמצעות getOrAwaitValue.
val value = tasksViewModel.newTaskEvent.getOrAwaitValue()
  1. הצהרת ערך שאינו Null.
assertThat(value.getContentIfNotHandled(), (not(nullValue())))

הבדיקה המלאה צריכה להיראות כמו הקוד הבא.

import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.example.android.architecture.blueprints.todoapp.getOrAwaitValue
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.not
import org.hamcrest.Matchers.nullValue
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith

@RunWith(AndroidJUnit4::class)
class TasksViewModelTest {

    @get:Rule
    var instantExecutorRule = InstantTaskExecutorRule()


    @Test
    fun addNewTask_setsNewTaskEvent() {
        // Given a fresh ViewModel
        val tasksViewModel = TasksViewModel(ApplicationProvider.getApplicationContext())

        // When adding a new task
        tasksViewModel.addNewTask()

        // Then the new task event is triggered
        val value = tasksViewModel.newTaskEvent.getOrAwaitValue()

        assertThat(value.getContentIfNotHandled(), not(nullValue()))


    }

}
  1. מפעילים את הקוד וצופים בכרטיס הבדיקה!

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

שלב 1. כתיבת בדיקה משלך ל-ViewModel

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

  1. לצורך שימוש ב-addNewTask_setsNewTaskEvent() לעיון, יש לכתוב בדיקה ב-TasksViewModelTest שנקראת setFilterAllTasks_tasksAddViewVisible() שמגדירה את מצב הסינון ל-ALL_TASKS, ומצהירה שה-DataData tasksAddViewVisible הוא true.


כדי להתחיל, צריך להשתמש בקוד שלמטה.

TasksViewModelTest

    @Test
    fun setFilterAllTasks_tasksAddViewVisible() {

        // Given a fresh ViewModel

        // When the filter type is ALL_TASKS

        // Then the "Add task" action is visible
        
    }

הערה:

  • מספר ה-TasksFilterType של כל המשימות הוא ALL_TASKS.
  • הרשאות הגישה של הלחצן להוספת משימה נשלטות על ידי LiveData tasksAddViewVisible.
  1. מריצים את הבדיקה.

שלב 2. השוואת הבדיקה לפתרון הבעיה

משווים את הפתרון שלכם לפתרון שלמטה.

TasksViewModelTest

    @Test
    fun setFilterAllTasks_tasksAddViewVisible() {

        // Given a fresh ViewModel
        val tasksViewModel = TasksViewModel(ApplicationProvider.getApplicationContext())

        // When the filter type is ALL_TASKS
        tasksViewModel.setFiltering(TasksFilterType.ALL_TASKS)

        // Then the "Add task" action is visible
        assertThat(tasksViewModel.tasksAddViewVisible.getOrAwaitValue(), `is`(true))
    }

בודקים אם:

  • יצרת את tasksViewModel עם אותה הצהרת ApplicationProvider.getApplicationContext()X ב-AndroidX.
  • בחרת להתקשר לשיטה setFiltering ולעבור את הערך בעמודה ALL_TASKS של סוג המסנן.
  • עליך לבדוק שה-tasksAddViewVisible נכון, באמצעות השיטה getOrAwaitNextValue.

שלב 3. הוספה של כלל @לפני

שימו לב איך בתחילת שתי הבדיקות אתם מגדירים TasksViewModel.

TasksViewModelTest

        // Given a fresh ViewModel
        val tasksViewModel = TasksViewModel(ApplicationProvider.getApplicationContext())

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

  1. יוצרים משתנה למכונה בשם lateinit שנקרא tasksViewModel|.
  2. יצירת שיטה שנקראת setupViewModel.
  3. סימון הערות עם @Before.
  4. העברת קוד אובייקט של מודל התצוגה ל-setupViewModel.

TasksViewModelTest

    // Subject under test
    private lateinit var tasksViewModel: TasksViewModel

    @Before
    fun setupViewModel() {
        tasksViewModel = TasksViewModel(ApplicationProvider.getApplicationContext())
    }
  1. רוצה להריץ את הקוד?

אזהרה

אין לבצע את הפעולות הבאות, אין להפעיל את

tasksViewModel

עם ההגדרה של:

val tasksViewModel = TasksViewModel(ApplicationProvider.getApplicationContext())

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

הקוד הסופי של TasksViewModelTest אמור להיראות כמו הקוד הבא.

TasksViewModelTest

@RunWith(AndroidJUnit4::class)
class TasksViewModelTest {

    // Subject under test
    private lateinit var tasksViewModel: TasksViewModel

    // Executes each task synchronously using Architecture Components.
    @get:Rule
    var instantExecutorRule = InstantTaskExecutorRule()

    @Before
    fun setupViewModel() {
        tasksViewModel = TasksViewModel(ApplicationProvider.getApplicationContext())
    }


    @Test
    fun addNewTask_setsNewTaskEvent() {

        // When adding a new task
        tasksViewModel.addNewTask()

        // Then the new task event is triggered
        val value = tasksViewModel.newTaskEvent.awaitNextValue()
        assertThat(
            value?.getContentIfNotHandled(), (not(nullValue()))
        )
    }

    @Test
    fun getTasksAddViewVisible() {

        // When the filter type is ALL_TASKS
        tasksViewModel.setFiltering(TasksFilterType.ALL_TASKS)

        // Then the "Add task" action is visible
        assertThat(tasksViewModel.tasksAddViewVisible.awaitNextValue(), `is`(true))
    }
    
}

אפשר ללחוץ כאן כדי לראות הבדל בין הקוד שהתחלת לבין הקוד הסופי.

כדי להוריד את הקוד עבור מעבדת הקוד הסופית, ניתן להשתמש בפקודת Git:

$ git clone https://github.com/googlecodelabs/android-testing.git
$ cd android-testing
$ git checkout end_codelab_1


לחלופין, אפשר להוריד את המאגר כקובץ ZIP, לפתוח אותו ב-Android Studio ולפתוח אותו.

להורדת קובץ Zip

מעבדת הקוד הזו כללה:

  • איך להריץ בדיקות מ-Android Studio.
  • ההבדל בין בדיקות מקומיות (test) לבין בדיקות אינסטרומנטציה (androidTest).
  • איך לכתוב בדיקות של יחידה מקומית באמצעות JUNIT ו-Hamcrest.
  • הגדרת בדיקות של ViewModel באמצעות ספריית הבדיקה של AndroidX.

קורס אוניברסיטה:

התיעוד של מפתח Android:

סרטי וידאו:

אחר:

קישורים למעבדות אחרות של הקוד בקורס הזה זמינים בדף הנחיתה של מכשירי Android מתקדמים בדף Kotlin codelabs.