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

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

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

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

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

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

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

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

קובעים את מיקום המקור

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

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

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

במקרים מסוימים, יכול להיות שלצוות שלכם יש מבני ספריות נוספים מתחת ל-tests בגלל הצורך לארוז חבילות שונות של בדיקות בקובצי APK נפרדים. במקרה כזה, תצטרכו ליצור ספריית משנה חדשה מתחת ל-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, אם היא חבילה נפרדת מהפלטפורמה עצמה.
<uses-library android:name="android.test.runner" />

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

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

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

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

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

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

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

בהגדרת הבדיקה אפשר לציין אפשרויות מיוחדות להגדרת המכשיר וארגומנטים שמוגדרים כברירת מחדל כדי לספק את מחלקת הבדיקה. אפשר לראות דוגמה בכתובת /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 שבה יש להשתמש כדי להריץ את הבדיקה, ומעביר את החבילה במכשיר להרצה ואת מסגרת ההרצה של הבדיקה, שהיא 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. כרגיל, שיטות בדיקה צריכות להיות ציבוריות, לא להצהיר על ערך החזרה, לא לקבל פרמטרים ויכולות להפעיל חריגים.

גישה למחלקת המכשירים

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

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

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

Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation()

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

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

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