ตัวอย่างการทดสอบที่มีเครื่องมือวัดของตัวเอง

เมื่อเริ่มการทดสอบการวัดคุม ระบบจะรีสตาร์ทแพ็กเกจเป้าหมายโดยแทรกโค้ดการวัดคุมและเริ่มการดําเนินการ ข้อยกเว้นอย่างหนึ่งคือแพ็กเกจเป้าหมายที่นี่ต้องไม่ใช่เฟรมเวิร์กแอปพลิเคชัน Android เอง เช่น แพ็กเกจ android เนื่องจากการดำเนินการดังกล่าวจะทำให้เกิดสถานการณ์ที่ขัดแย้งกันซึ่งจะต้องรีสตาร์ทเฟรมเวิร์ก Android ซึ่งเป็นส่วนสนับสนุนฟังก์ชันของระบบ รวมถึงเครื่องมือวัดผลเอง

ซึ่งหมายความว่าการทดสอบการใช้เครื่องมือจะไม่สามารถแทรกตัวเองลงในเฟรมเวิร์ก Android หรือที่เรียกว่าเซิร์ฟเวอร์ระบบเพื่อเรียกใช้ หากต้องการทดสอบเฟรมเวิร์ก Android โค้ดทดสอบจะเรียกใช้ได้เฉพาะแพลตฟอร์ม API สาธารณะหรือแพลตฟอร์มที่เปิดเผยโดยใช้ภาษาคำจำกัดความอินเทอร์เฟซ Android (AIDL) ที่มีอยู่ในซอร์สโค้ดของแพลตฟอร์ม สําหรับการทดสอบหมวดหมู่นี้ การกำหนดเป้าหมายไปยังแพ็กเกจใดแพ็กเกจหนึ่งไม่มีความหมาย ดังนั้น ธรรมเนียมปฏิบัติจึงกำหนดให้ประกาศเครื่องมือดังกล่าวเพื่อกำหนดเป้าหมายไปยังแพ็กเกจแอปพลิเคชันทดสอบของตนเองตามที่ระบุไว้ในแท็ก <manifest> ของ AndroidManifest.xml

แพ็กเกจแอปพลิเคชันทดสอบในหมวดหมู่นี้อาจมีลักษณะต่อไปนี้ด้วย ทั้งนี้ขึ้นอยู่กับข้อกำหนด

  • กิจกรรมแบบกลุ่มที่จําเป็นสําหรับการทดสอบ
  • แชร์รหัสผู้ใช้กับระบบ
  • ลงนามด้วยคีย์แพลตฟอร์ม
  • คอมไพล์จากซอร์สโค้ดเฟรมเวิร์กแทน SDK สาธารณะ

บางครั้งการทดสอบเครื่องมือวัดผลในหมวดหมู่นี้เรียกว่าการวัดผลด้วยตนเอง ตัวอย่างการทดสอบเครื่องมือวัดผลด้วยตนเองในซอร์สโค้ดของแพลตฟอร์มมีดังนี้

ตัวอย่างที่กล่าวถึงในที่นี้คือ การเขียนการทดสอบเครื่องมือวัดใหม่ที่มีการตั้งค่าแพ็กเกจเป้าหมายเป็นแพ็กเกจแอปพลิเคชันทดสอบของตัวเอง คู่มือนี้ใช้การทดสอบต่อไปนี้เป็นตัวอย่าง

เราขอแนะนำให้เรียกดูโค้ดก่อนเพื่อให้ทราบภาพคร่าวๆ ก่อนดำเนินการต่อ

เลือกตำแหน่งของแหล่งที่มา

โดยปกติแล้ว ทีมของคุณจะมีรูปแบบที่ชัดเจนสำหรับตำแหน่งที่จะตรวจสอบโค้ดและตำแหน่งที่จะเพิ่มการทดสอบ ทีมส่วนใหญ่เป็นเจ้าของที่เก็บ Git รายการเดียว หรือแชร์ที่เก็บกับทีมอื่นๆ แต่มีไดเรกทอรีย่อยเฉพาะที่มีซอร์สโค้ดของคอมโพเนนต์

สมมติว่าตำแหน่งรูทของแหล่งที่มาของคอมโพเนนต์อยู่ที่ <component source root> คอมโพเนนต์ส่วนใหญ่จะมีโฟลเดอร์ src และ tests อยู่ด้านล่าง รวมถึงไฟล์เพิ่มเติมบางไฟล์ เช่น Android.mk (หรือแยกออกเป็นไฟล์ .mk เพิ่มเติม) ไฟล์ Manifest AndroidManifest.xml และไฟล์การกําหนดค่าการทดสอบ 'AndroidTest.xml'

เนื่องจากคุณกําลังเพิ่มการทดสอบใหม่ คุณอาจต้องสร้างไดเรกทอรี tests ข้างคอมโพเนนต์ src แล้วป้อนข้อมูลลงในไดเรกทอรี

ในบางกรณี ทีมของคุณอาจมีโครงสร้างไดเรกทอรีเพิ่มเติมในส่วน tests เนื่องจากต้องจัดแพ็กเกจชุดการทดสอบต่างๆ เป็น APK แต่ละรายการ และในกรณีนี้ คุณจะต้องสร้างไดเรกทอรีย่อยใหม่ใน tests

ไม่ว่าโครงสร้างจะเป็นอย่างไร สุดท้ายแล้วคุณจะต้องป้อนข้อมูลในไดเรกทอรี tests หรือไดเรกทอรีย่อยที่สร้างขึ้นใหม่ด้วยไฟล์ที่คล้ายกับไฟล์ในไดเรกทอรี instrumentation ในตัวอย่างการเปลี่ยนแปลงใน Gerrit รายละเอียดของไฟล์แต่ละไฟล์จะอธิบายไว้ในส่วนถัดไปของเอกสารนี้

ไฟล์ Manifest

เช่นเดียวกับโปรเจ็กต์แอป แต่ละข้อบังคับของเครื่องมือทดสอบจะต้องมีไฟล์ Manifest ที่ชื่อ AndroidManifest.xml หากต้องการรวมไฟล์นี้โดยอัตโนมัติโดยใช้ไฟล์ make หลักBUILD_PACKAGE ให้ใส่ไฟล์นี้ไว้ข้างไฟล์Android.mkสําหรับโมดูลทดสอบ

หากไม่คุ้นเคยกับไฟล์ AndroidManifest.xml โปรดดูภาพรวมของไฟล์ Manifest ของแอป

ต่อไปนี้เป็นตัวอย่างไฟล์ 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

<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 ในส่วนก่อนหน้า) แต่ทั้ง 2 อย่างนี้เป็นแนวคิดที่แตกต่างกัน

  • สิทธิ์หรือ API บางรายการได้รับการปกป้องด้วยลายเซ็น ซึ่งต้องใช้ใบรับรองการลงนามเดียวกัน
  • สิทธิ์หรือ API บางรายการกำหนดให้ต้องมีข้อมูลประจำตัวของผู้ใช้ system ของผู้เรียก ซึ่งกำหนดให้แพ็กเกจที่เรียกใช้ต้องแชร์รหัสผู้ใช้กับ system หากเป็นแพ็กเกจแยกต่างหากจากแพลตฟอร์มหลัก
<uses-library android:name="android.test.runner" />

รายการนี้จําเป็นสําหรับการทดสอบเครื่องมือวัดผลทั้งหมด เนื่องจากคลาสที่เกี่ยวข้องจะบรรจุอยู่ในไฟล์ไลบรารี JAR ของเฟรมเวิร์กแยกต่างหาก จึงจําเป็นต้องมีรายการ classpath เพิ่มเติมเมื่อเฟรมเวิร์กแอปพลิเคชันเรียกใช้แพ็กเกจทดสอบ

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

คุณอาจสังเกตเห็นว่า targetPackage ที่นี่มีการประกาศเหมือนกับแอตทริบิวต์ package ที่ประกาศในแท็ก manifest ของไฟล์นี้ ตามที่ระบุไว้ในพื้นฐานการทดสอบ การทดสอบเครื่องมือวัดผลในหมวดหมู่นี้มักมีไว้เพื่อทดสอบเฟรมเวิร์ก API ดังนั้นจึงไม่มีความหมายมากนักที่การทดสอบเหล่านี้จะมีแพ็กเกจแอปพลิเคชันที่กำหนดเป้าหมายโดยเฉพาะนอกเหนือจากตัวมันเอง

ไฟล์การกําหนดค่าแบบง่าย

โมดูลทดสอบใหม่แต่ละรายการต้องมีไฟล์การกําหนดค่าเพื่อควบคุมระบบการสร้างด้วยข้อมูลเมตาของโมดูล ไลบรารีที่ใช้ร่วมกันขณะคอมไพล์ และวิธีการจัดแพ็กเกจ ในกรณีส่วนใหญ่ ตัวเลือกไฟล์ 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 ที่จะใช้ในการทดสอบ และส่งผ่านแพ็กเกจในอุปกรณ์ที่จะทำการทดสอบและเฟรมเวิร์กโปรแกรมรันทดสอบ ซึ่งในกรณีนี้คือ 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() {
    ...

JUnit4 ใช้หมายเหตุ @Before และ @After กับเมธอดเพื่อดำเนินการตั้งค่าก่อนการทดสอบและการทดสอบหลังการทดสอบ ในทํานองเดียวกัน JUnit4 จะใช้คําอธิบายประกอบ @BeforeClass และ @AfterClass กับเมธอดเพื่อดําเนินการตั้งค่าก่อนเรียกใช้การทดสอบทั้งหมดในคลาสทดสอบ และปิดใช้งานหลังจากนั้น โปรดทราบว่าวิธีการตั้งค่าและเลิกใช้งานระดับคลาสต้องเป็นแบบคงที่ สำหรับวิธีการทดสอบ ไม่จำเป็นต้องขึ้นต้นชื่อวิธีการด้วย test อีกต่อไป ต่างจาก JUnit เวอร์ชันเก่า แต่ต้องกำกับเนื้อหาด้วย @Test ตามปกติแล้ว วิธีการทดสอบต้องเป็นแบบสาธารณะ ไม่ประกาศค่าที่แสดงผล ไม่รับพารามิเตอร์ และอาจแสดงข้อยกเว้น

การเข้าถึงคลาสเครื่องมือวัด

แม้ว่าจะไม่ได้กล่าวถึงในตัวอย่าง Hello World พื้นฐาน แต่การทดสอบ Android มักกำหนดให้ต้องเข้าถึงอินสแตนซ์ Instrumentation ซึ่งเป็นอินเทอร์เฟซ API หลักที่ให้สิทธิ์เข้าถึงบริบทแอปพลิเคชัน, API การทดสอบที่เกี่ยวข้องกับวงจรของกิจกรรม และอื่นๆ

เนื่องจากการทดสอบ JUnit4 ไม่จำเป็นต้องใช้คลาสฐานทั่วไปอีกต่อไป คุณจึงไม่จำเป็นต้องรับอินสแตนซ์ Instrumentation ผ่าน InstrumentationTestCase#getInstrumentation() แล้ว แต่เครื่องมือรันไทม์การทดสอบใหม่จะจัดการผ่าน InstrumentationRegistry ซึ่งจะจัดเก็บการตั้งค่าตามบริบทและสภาพแวดล้อมที่เฟรมเวิร์กเครื่องมือวัดผลสร้างขึ้น

หากต้องการเข้าถึงอินสแตนซ์ของคลาส Instrumentation ให้เรียกใช้เมธอดแบบคงที่ getInstrumentation() ในคลาส InstrumentationRegistry ดังนี้

Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation()

สร้างและทดสอบในเครื่อง

สําหรับ Use Case ที่พบบ่อยที่สุด ให้ใช้ Atest

สําหรับกรณีที่ซับซ้อนมากขึ้นซึ่งต้องมีการปรับแต่งมากขึ้น ให้ทําตามวิธีการเครื่องมือวัด