דוגמה לבדיקות עם כלי למדידת ביצועים עצמאיים

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

כלומר, בדיקת כלי למדידת ביצועים לא יכולה להחדיר את עצמה למסגרת Android, שנקראת גם שרת המערכת, לצורך ביצוע. כדי לבדוק את המסגרת של Android, קוד הבדיקה יכול להפעיל רק ממשקי API ציבוריים, או ממשקים שנחשפו באמצעות שפת הגדרת הממשק של Android‏ AIDL שזמינה בעץ המקור של הפלטפורמה. בקטגוריה הזו של בדיקות, אין משמעות לטרגוט של חבילה מסוימת. לכן, מקובל להצהיר על מכשירי המדידה האלה כמיועדים לטירגוט של חבילת אפליקציית הבדיקה שלהם, כפי שהוגדר בתג <manifest> של AndroidManifest.xml.

בהתאם לדרישות, חבילות של אפליקציות בדיקה בקטגוריה הזו יכולות גם:

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

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

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

מומלץ לעיין בקוד קודם כדי לקבל תמונה כללית לפני שממשיכים.

בחירת מיקום מקור

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

בהנחה שמיקום השורש של מקור הרכיבים הוא <component source root>, לרוב הרכיבים יש תיקיות src ו-tests מתחתיו, וגם כמה קבצים נוספים כמו Android.mk (או קבצים .mk נוספים), קובץ המניפסט AndroidManifest.xml וקובץ הגדרת הבדיקה AndroidTest.xml.

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

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

ללא קשר למבנה, בסופו של דבר תצטרכו לאכלס את הספרייה tests או את ספריית המשנה החדשה שנוצרה בקבצים דומים לאלה שבספרייה instrumentation בשינוי לדוגמה ב-Gerrit. הפרטים של כל קובץ מוסברים בהמשך המאמר.

קובץ מניפסט

בדומה לפרויקט אפליקציה, לכל מודול של בדיקת מכשור נדרש קובץ מניפסט שנקרא AndroidManifest.xml. כדי לכלול את הקובץ הזה באופן אוטומטי באמצעות קובץ ה-makefile של הליבה BUILD_PACKAGE, צריך לספק את הקובץ הזה לצד הקובץ Android.mk של מודול הבדיקה.

אם אתם לא מכירים את הקובץ AndroidManifest.xml, תוכלו לעיין בסקירה הכללית על קובץ המניפסט של האפליקציה.

לפניכם קובץ AndroidManifest.xml לדוגמה:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
  android:sharedUserId="android.uid.system"
  package="android.test.example.helloworld" >

    <application>
       <uses-library android:name="android.test.runner"/>
    </application>

    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
                     android:targetPackage="android.test.example.helloworld"
                     android:label="Hello World Test"/>

</manifest>

הערות נבחרות לגבי קובץ המניפסט:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="android.test.example.helloworld" >

מאפיין package הוא שם החבילה של האפליקציה: זהו המזהה הייחודי שמשמש את מסגרת האפליקציות של Android לזיהוי אפליקציה (או בהקשר הזה: אפליקציית הבדיקה). כל משתמש במערכת יכול להתקין רק אפליקציה אחת עם שם החבילה הזה.

בנוסף, המאפיין package זהה למה שמוחזר על ידי ComponentName#getPackageName(), וגם זה שמשתמשים בו כדי לקיים אינטראקציה עם פקודות משנה שונות של pm באמצעות adb shell.

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

android:sharedUserId="android.uid.system"

ההצהרה הזו קובעת שבזמן ההתקנה, לקובץ ה-APK הזה צריך להקצות את אותו מזהה משתמש, כלומר זהות בסביבת זמן הריצה, כמו לפלטפורמת הליבה. חשוב לזכור שהדבר תלוי בכך שקובץ ה-APK חתום באותו אישור שבו חתומה הפלטפורמה המרכזית (ראו LOCAL_CERTIFICATE בקטע הקודם), אבל מדובר במושגים שונים:

  • הרשאות או ממשקי API מסוימים מוגנים באמצעות חתימה, ולכן נדרשת אותה אישור חתימה
  • חלק מההרשאות או ממשקי ה-API דורשים את זהות המשתמש system של מבצע הקריאה החוזרת, ולכן חבילת הקריאה החוזרת צריכה לשתף את מזהה המשתמש עם system, אם היא חבילת API נפרדת מהפלטפורמה עצמה
<uses-library android:name="android.test.runner" />

הדבר נדרש לכל בדיקות ה-Instrumentation כי הכיתות הקשורות ארוזות בקובץ ספרייה נפרד של framework JAR, ולכן נדרשות רשומות נוספות ב-classpath כשחבילת הבדיקה מופעלת על ידי מסגרת האפליקציה.

android:targetPackage="android.test.example.helloworld"

יכול להיות ששמתם לב שהמאפיין targetPackage שמוצהר כאן זהה למאפיין package שמוצהר בתג manifest בקובץ הזה. כפי שצוין בקטע יסודות הבדיקה, קטגוריית הבדיקות הזו מיועדת בדרך כלל לבדיקת ממשקי API של מסגרות, ולכן אין הרבה משמעות לכך שתהיה להן חבילת אפליקציות ספציפית שמוגדרת כמטרה, מלבד עצמה.

קובץ תצורה פשוט

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

קובץ תצורה מורכב

במקרים מורכבים יותר, צריך גם לכתוב קובץ תצורה של בדיקה ל-Trade Federation, ערכת הבדיקה של Android.

בהגדרת הבדיקה אפשר לציין אפשרויות מיוחדות להגדרת המכשיר וארגומנטים שמוגדרים כברירת מחדל כדי לספק את הכיתה של הבדיקה. אפשר לעיין בדוגמה בקובץ /platform_testing/tests/example/instrumentation/AndroidTest.xml.

צירפנו כאן תמונת מצב לצורך נוחות:

<configuration description="Runs sample instrumentation test.">
  <target_preparer class="com.android.tradefed.targetprep.TestFilePushSetup"/>
  <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
    <option name="test-file-name" value="HelloWorldTests.apk"/>
  </target_preparer>
  <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"/>
  <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"/>
  <option name="test-suite-tag" value="apct"/>
  <option name="test-tag" value="SampleInstrumentationTest"/>

  <test class="com.android.tradefed.testtype.AndroidJUnitTest">
    <option name="package" value="android.test.example.helloworld"/>
    <option name="runner" value="android.support.test.runner.AndroidJUnitRunner"/>
  </test>
</configuration>

הערות נבחרות לגבי קובץ התצורה של הבדיקה:

<target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
  <option name="test-file-name" value="HelloWorldTests.apk"/>
</target_preparer>

הפקודה הזו מורה ל-Trade Federation להתקין את HelloWorldTests.apk במכשיר היעד באמצעות target_preparer שצוין. למפתחים יש גישה לכלי רבים להכנת מטרות ב-Trade Federation, שבעזרתם אפשר לוודא שהמכשיר מוגדר כראוי לפני ביצוע הבדיקה.

<test class="com.android.tradefed.testtype.AndroidJUnitTest">
  <option name="package" value="android.test.example.helloworld"/>
  <option name="runner" value="android.support.test.runner.AndroidJUnitRunner"/>
</test>

כאן מצוין סוג הבדיקה של Trade Federation שמשמש להרצת הבדיקה, והוא מעביר את החבילה במכשיר להרצה ואת מסגרת ה-test runner, שהיא JUnit במקרה הזה.

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

תכונות של JUnit4

שימוש בספרייה android-support-test ככלי להרצת בדיקות מאפשר להשתמש בקטגוריות בדיקה חדשות בסגנון JUnit4, והשינוי לדוגמה ב-Gerrit מכיל שימוש בסיסי מאוד בתכונות שלה. אפשר לעיין בדוגמה שבכתובת /platform_testing/tests/example/instrumentation/src/android/test/example/helloworld/HelloWorldTest.java.

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

@RunWith(JUnit4.class)
public class HelloWorldTest {

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

    @BeforeClass
    public static void beforeClass() {
    ...
    @AfterClass
    public static void afterClass() {
    ...
    @Before
    public void before() {
    ...
    @After
    public void after() {
    ...
    @Test
    @SmallTest
    public void testHelloWorld() {
    ...

ההערות @Before ו-@After משמשות את JUnit4 בשיטות כדי לבצע הגדרה לפני הבדיקה ופירוק אחרי הבדיקה. באופן דומה, ההערות @BeforeClass ו-@AfterClass משמשות את JUnit4 בשיטות כדי לבצע הגדרה לפני ביצוע כל הבדיקות בכיתה לבדיקה, ולבצע ניתוק לאחר מכן. חשוב לזכור ששיטות ההגדרה והפירוק ברמת הכיתה חייבות להיות סטטיות. לגבי שיטות הבדיקה, בניגוד לגרסאות קודמות של JUnit, שם השיטה כבר לא צריך להתחיל ב-test. במקום זאת, צריך להוסיף הערה לכל אחת מהן עם @Test. כרגיל, שיטות הבדיקה צריכות להיות ציבוריות, לא להצהיר על ערך החזרה, לא לקבל פרמטרים ויכולות להוציא חריגות.

גישה לכיתה של מכשירי המדידה

בדוגמה הבסיסית של hello world לא מוסבר על כך, אבל לעתים קרובות נדרש למבדק Android מופע של Instrumentation: זהו ממשק ה-API המרכזי שמספק גישה להקשרים של אפליקציות, לממשקי API למבדקים שקשורים למחזור החיים של הפעילות ועוד.

מאחר שבבדיקות JUnit4 כבר אין צורך בכיתה בסיסית משותפת, אין יותר צורך לקבל מופע של Instrumentation דרך InstrumentationTestCase#getInstrumentation(). במקום זאת, מנהל הבדיקות החדש מנהל אותו דרך InstrumentationRegistry, שבו מאוחסן ההגדרה ההקשרית והסביבתית שנוצרה על ידי מסגרת המדידה.

כדי לגשת למופעים של המחלקה Instrumentation, פשוט קוראים לשיטה הסטטית getInstrumentation() במחלקה InstrumentationRegistry:

Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation()

פיתוח ובדיקה באופן מקומי

בתרחישים הנפוצים ביותר לדוגמה, כדאי להשתמש ב-Atest.

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