نمونه تست های خودآزمایی

وقتی یک تست ابزار دقیق شروع می‌شود، بسته هدف آن با تزریق کد ابزار دقیق، مجدداً راه‌اندازی شده و برای اجرا آماده می‌شود. یک استثنا این است که بسته هدف در اینجا نمی‌تواند خود چارچوب برنامه اندروید، مانند بسته android باشد، زیرا انجام این کار منجر به وضعیت متناقضی می‌شود که در آن چارچوب اندروید نیاز به راه‌اندازی مجدد دارد، که همان چیزی است که از عملکردهای سیستم، از جمله خود ابزار دقیق، پشتیبانی می‌کند.

این بدان معناست که یک تست ابزار دقیق نمی‌تواند خود را برای اجرا به چارچوب اندروید، که همان سرور سیستم است، تزریق کند. برای تست چارچوب اندروید، کد تست می‌تواند فقط سطوح API عمومی یا سطوحی را که با استفاده از زبان تعریف رابط اندروید AIDL موجود در درخت منبع پلتفرم در معرض دید قرار می‌گیرند، فراخوانی کند. برای این دسته از تست‌ها، هدف قرار دادن هیچ بسته خاصی بی‌معنی است. بنابراین، مرسوم است که چنین ابزارهایی به گونه‌ای اعلام شوند که بسته برنامه تست خود را هدف قرار دهند، همانطور که در برچسب <manifest> خود در AndroidManifest.xml تعریف شده است.

بسته به الزامات، بسته‌های درخواست آزمون در این دسته همچنین ممکن است:

  • فعالیت‌های مورد نیاز برای آزمایش را دسته‌بندی کنید.
  • شناسه کاربری را با سیستم به اشتراک بگذارید.
  • با کلید پلتفرم امضا شود.
  • به جای SDK عمومی، بر اساس سورس فریم‌ورک کامپایل شود.

این دسته از تست‌های ابزار دقیق گاهی اوقات به عنوان خود-ابزارشناسی (self-instrumentation) شناخته می‌شوند. در اینجا چند نمونه از تست‌های خود-ابزارشناسی در منبع پلتفرم آورده شده است:

مثالی که در اینجا پوشش داده شده است، نوشتن یک تست ابزار دقیق جدید با مجموعه بسته هدف در بسته برنامه تست خود است. این راهنما از تست زیر به عنوان مثال استفاده می‌کند:

توصیه می‌شود قبل از ادامه، ابتدا کد را مرور کنید تا یک تصور کلی از آن داشته باشید.

در مورد محل منبع تصمیم بگیرید

معمولاً تیم شما از قبل الگوی مشخصی از مکان‌ها برای بررسی کد و مکان‌هایی برای اضافه کردن تست‌ها دارد. اکثر تیم‌ها یک مخزن گیت دارند یا آن را با تیم‌های دیگر به اشتراک می‌گذارند، اما یک زیرشاخه اختصاصی دارند که شامل کد منبع کامپوننت است.

با فرض اینکه محل ریشه منبع کامپوننت شما در <component source root> باشد، اکثر کامپوننت‌ها دارای پوشه‌های src و tests در زیر خود هستند، و برخی فایل‌های اضافی مانند Android.mk (یا تقسیم شده به فایل‌های .mk اضافی)، فایل مانیفست AndroidManifest.xml و فایل پیکربندی تست 'AndroidTest.xml' نیز در زیر آن قرار دارند.

از آنجایی که شما در حال اضافه کردن یک تست کاملاً جدید هستید، احتمالاً باید دایرکتوری tests را در کنار کامپوننت src خود ایجاد کنید و آن را با محتوا پر کنید.

در برخی موارد، تیم شما ممکن است به دلیل نیاز به بسته‌بندی مجموعه‌های مختلف تست‌ها در فایل‌های apk جداگانه، ساختارهای دایرکتوری بیشتری در زیر tests داشته باشد. و در این حالت، شما باید یک زیر دایرکتوری جدید در زیر tests ایجاد کنید.

صرف نظر از ساختار، در نهایت دایرکتوری tests یا زیر دایرکتوری تازه ایجاد شده را با فایل‌هایی مشابه آنچه در دایرکتوری instrumentation در نمونه تغییر gerrit وجود دارد، پر خواهید کرد. جزئیات هر فایل بعداً در این سند توضیح داده شده است.

فایل مانیفست

همانند یک پروژه اپلیکیشن، هر ماژول تست ابزار دقیق به یک فایل مانیفست به نام AndroidManifest.xml نیاز دارد. برای اینکه این فایل به طور خودکار با استفاده از فایل makefile هسته BUILD_PACKAGE اضافه شود، این فایل را در کنار فایل Android.mk ماژول تست خود قرار دهید.

اگر با فایل AndroidManifest.xml آشنا نیستید، به نمای کلی App 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 xmlns:android="http://schemas.android.com/apk/res/android"
    package="android.test.example.helloworld" >

ویژگی package نام بسته برنامه است: این شناسه منحصر به فردی است که چارچوب برنامه اندروید برای شناسایی یک برنامه (یا در این زمینه: برنامه آزمایشی شما) از آن استفاده می‌کند. هر کاربر در سیستم فقط می‌تواند یک برنامه با آن نام بسته نصب کند.

علاوه بر این، این ویژگی package همان چیزی است که ComponentName#getPackageName() برمی‌گرداند، و همچنین همان چیزی است که شما برای تعامل با دستورات مختلف pm sub از آن استفاده می‌کنید، مانند adb shell .

توجه داشته باشید که اگرچه نام بسته معمولاً به همان سبک نام بسته جاوا است، اما در واقع ارتباط بسیار کمی با آن دارد. به عبارت دیگر، بسته برنامه (یا تست) شما ممکن است شامل کلاس‌هایی با هر نام بسته‌ای باشد، اگرچه از طرف دیگر، می‌توانید سادگی را انتخاب کنید و نام بسته جاوای سطح بالای خود را در برنامه یا تست خود با نام بسته برنامه یکسان داشته باشید.

android:sharedUserId="android.uid.system"

این کد اعلام می‌کند که در زمان نصب، به این فایل APK باید همان شناسه کاربری، یعنی هویت زمان اجرا، که در پلتفرم اصلی وجود دارد، اعطا شود. توجه داشته باشید که این امر منوط به آن است که apk با همان گواهی پلتفرم اصلی امضا شده باشد ( LOCAL_CERTIFICATE در بخش قبلی مراجعه کنید)، با این حال، این دو مفهوم متفاوتی دارند:

  • برخی از مجوزها یا APIها دارای حفاظت امضا هستند که به همان گواهی امضا نیاز دارد
  • برخی از مجوزها یا APIها به هویت کاربر system فراخواننده نیاز دارند، که مستلزم آن است که بسته فراخوانی‌کننده، در صورتی که بسته‌ای جدا از خود پلتفرم اصلی باشد، شناسه کاربر را با system به اشتراک بگذارد.
<uses-library android:name="android.test.runner" />

این برای همه تست‌های Instrumentation الزامی است زیرا کلاس‌های مرتبط در یک فایل کتابخانه JAR فریم‌ورک جداگانه بسته‌بندی شده‌اند، بنابراین هنگام فراخوانی بسته تست توسط فریم‌ورک برنامه، به ورودی‌های classpath اضافی نیاز است.

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

شاید متوجه شده باشید که targetPackage در اینجا همانند ویژگی package که در تگ manifest این فایل تعریف شده است، تعریف شده است. همانطور که در بخش اصول تست ذکر شد، این دسته از تست‌های ابزار دقیق معمولاً برای تست APIهای فریم‌ورک در نظر گرفته می‌شوند، بنابراین داشتن یک بسته‌ی کاربردی هدفمند خاص، غیر از خودش، برای آنها چندان معنی‌دار نیست.

فایل پیکربندی ساده

هر ماژول آزمایشی جدید باید یک فایل پیکربندی داشته باشد تا سیستم ساخت را با ابرداده‌های ماژول، وابستگی‌های زمان کامپایل و دستورالعمل‌های بسته‌بندی هدایت کند. در بیشتر موارد، گزینه فایل Blueprint مبتنی بر Soong کافی است. برای جزئیات بیشتر، به Simple Test Configuration مراجعه کنید.

فایل پیکربندی پیچیده

برای این موارد پیچیده‌تر، شما همچنین باید یک فایل پیکربندی آزمایشی برای ابزار تست اندروید، 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 این است که تست‌ها دیگر نیازی به ارث‌بری از یک کلاس تست پایه مشترک ندارند؛ در عوض، شما تست‌ها را در کلاس‌های ساده جاوا می‌نویسید و از حاشیه‌نویسی برای نشان دادن تنظیمات و محدودیت‌های خاص تست استفاده می‌کنید. در این مثال، ما دستور می‌دهیم که این کلاس باید به عنوان یک تست 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 پوشش داده نشده است، اما برای یک تست اندروید، دسترسی به نمونه‌ی Instrumentation نسبتاً رایج است: این رابط API اصلی است که دسترسی به زمینه‌های برنامه، APIهای تست مرتبط با چرخه‌ی حیات فعالیت و موارد دیگر را فراهم می‌کند.

از آنجا که تست‌های JUnit4 دیگر به یک کلاس پایه مشترک نیاز ندارند، دیگر نیازی به دریافت نمونه Instrumentation از طریق InstrumentationTestCase#getInstrumentation() نیست، در عوض، اجراکننده تست جدید آن را از طریق InstrumentationRegistry مدیریت می‌کند که در آن تنظیمات زمینه‌ای و محیطی ایجاد شده توسط چارچوب instrumentation ذخیره می‌شود.

برای دسترسی به نمونه کلاس Instrumentation ، کافیست متد استاتیک getInstrumentation() را روی کلاس InstrumentationRegistry فراخوانی کنید:

Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation()

ساخت و آزمایش به صورت محلی

برای رایج‌ترین موارد استفاده، از Atest استفاده کنید.

برای موارد پیچیده‌تر که نیاز به سفارشی‌سازی سنگین‌تری دارند، دستورالعمل‌های ابزار دقیق را دنبال کنید.