ساختن آزمایش‌های واحد محلی

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

آزمایش واحد رفتار بخش کوچکی از کد را درستی‌سنجی می‌کند، واحد تحت آزمایش. این کار را با اجرای آن کد و بررسی نتیجه انجام می‌دهد.

آزمایش‌های واحد معمولاً ساده هستند اما راه‌اندازی آن‌ها می‌تواند مشکل‌ساز باشد، به‌ویژه زمانی که واحد تحت آزمایش با درنظر گرفتن قابلیت آزمایش طراحی نشده باشد:

  • کدی که می‌خواهید درستی‌سنجی کنید باید ازطریق آزمایش قابل‌دسترسی باشد. برای مثال، نمی‌توانید یک روش خصوصی را مستقیماً آزمایش کنید. درعوض، کلاس را بااستفاده از میاناهای برنامه‌سازی کاربردی عمومی آن آزمایش می‌کنید.
  • برای اجرای آزمون‌های واحد در جداسازی، وابستگی‌های واحد تحت آزمون باید با مؤلفه‌هایی که شما کنترل می‌کنید، مانند جعلی‌ها یا دیگر بدل‌های آزمون جایگزین شوند. این موضوع به‌ویژه زمانی مشکل‌ساز است که کد شما به چارچوب Android وابسته باشد.

برای آشنایی با راهبردهای رایج آزمایش واحد در Android، مقاله چه چیزی را آزمایش کنیم را بخوانید.

مکان آزمایش‌های محلی

به‌طور پیش‌فرض، فایل‌های منبع برای آزمون‌های واحد محلی در module-name/src/test/ قرار می‌گیرند. وقتی پروژه جدیدی بااستفاده از «استودیو Android» ایجاد می‌کنید، این دایرکتوری ازقبل وجود دارد.

درحال افزودن وابستگی‌های آزمایشی

همچنین باید وابستگی‌های آزمایشی را برای پروژه‌تان پیکربندی کنید تا از میاناهای برنامه‌سازی کاربردی استاندارد ارائه‌شده توسط چارچوب آزمایشی JUnit استفاده کنید.

برای انجام این کار، فایل build.gradle واحد برنامه‌تان را باز کنید و کتابخانه‌های زیر را به‌عنوان وابستگی‌ها مشخص کنید. از تابع testImplementation استفاده کنید تا نشان دهید که آن‌ها برای مجموعه منبع آزمایشی محلی اعمال می‌شوند و نه برنامه:

dependencies {
  // Required -- JUnit 4 framework
  testImplementation "junit:junit:$jUnitVersion"
  // Optional -- Robolectric environment
  testImplementation "androidx.test:core:$androidXTestVersion"
  // Optional -- Mockito framework
  testImplementation "org.mockito:mockito-core:$mockitoVersion"
  // Optional -- mockito-kotlin
  testImplementation "org.mockito.kotlin:mockito-kotlin:$mockitoKotlinVersion"
  // Optional -- Mockk framework
  testImplementation "io.mockk:mockk:$mockkVersion"
}

ایجاد کلاس آزمون واحد محلی

کلاس آزمون واحد محلی خود را به‌عنوان کلاس آزمون JUnit 4 می‌نویسید.

برای انجام این کار، کلاسی ایجاد کنید که حاوی یک یا چند روش آزمایشی باشد، معمولاً در module-name/src/test/. روش آزمایشی با حاشیه‌نویسی @Test شروع می‌شود و حاوی کدی برای تمرین و درستی‌سنجی یک جنبه از مؤلفه‌ای است که می‌خواهید آزمایش کنید.

مثال زیر نحوه پیاده‌سازی کلاس آزمون واحد محلی را نشان می‌دهد. روش آزمایش emailValidator_correctEmailSimple_returnsTrue()تلاش می‌کند isValidEmail() را درستی‌سنجی کند، که روشی درون برنامه است. اگر isValidEmail() نیز درست برگرداند، تابع آزمایش درست برمی‌گرداند.

کاتلین

import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Test

class EmailValidatorTest {
  @Test fun emailValidator_CorrectEmailSimple_ReturnsTrue() {
    assertTrue(EmailValidator.isValidEmail("name@email.com"))
  }

}

جاوا

import org.junit.Test;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

class EmailValidatorTest {
  @Test
  public void emailValidator_CorrectEmailSimple_ReturnsTrue() {
    assertTrue(EmailValidator.isValidEmail("name@email.com"));
  }
}

باید آزمایش‌های خوانایی ایجاد کنید که ارزیابی کند آیا مؤلفه‌های موجود در برنامه‌تان نتایج موردانتظار را برمی‌گرداند یا نه. توصیه می‌کنیم از کتابخانه ادعاها مثل junit.Assert،‏ Hamcrest، یا Truth استفاده کنید. تکه‌کد بالا نمونه‌ای از نحوه استفاده از junit.Assert است.

کتابخانه Android قابل‌شبیه‌سازی

وقتی آزمایش‌های واحد محلی را اجرا می‌کنید، افزونه Android Gradle شامل کتابخانه‌ای است که همه میاناهای برنامه‌سازی کاربردی چارچوب Android را دربرمی‌گیرد و با نسخه‌ای که در پروژه‌تان استفاده می‌شود مطابقت دارد. این کتابخانه همه روش‌ها و کلاس‌های عمومی آن میاناهای برنامه‌سازی کاربردی را دربرمی‌گیرد، اما کد داخل روش‌ها برداشته شده است. اگر به هرکدام از روش‌ها دسترسی پیدا شود، آزمایش با استثنا روبرو می‌شود.

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

درحال شبیه‌سازی وابستگی‌های Android

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

با کتابخانه Mockable Android و چارچوب‌های شبیه‌سازی مانند Mockito یا MockK، می‌توانید رفتار شبیه‌سازی‌های کلاس‌های Android را در آزمون‌های واحد خود برنامه‌ریزی کنید.

برای افزودن شیء ساختگی به آزمایش واحد محلی بااستفاده از Mockito، این مدل برنامه‌نویسی را دنبال کنید:

  1. وابستگی کتابخانه Mockito را در فایل build.gradle خود اضافه کنید، همان‌طور که در راه‌اندازی محیط آزمایشی توضیح داده شده است.
  2. در ابتدای تعریف کلاس آزمون واحد، حاشیه‌نویسی @RunWith(MockitoJUnitRunner.class) را اضافه کنید. این حاشیه‌نویسی به اجراکننده آزمایش Mockito می‌گوید که استفاده شما از چارچوب را اعتبارسنجی کند و مقداردهی اولیه اشیای ساختگی شما را ساده می‌کند.
  3. برای ایجاد شیء ساختگی برای وابستگی Android، حاشیه‌نویسی @Mock را قبل‌از اعلام فیلد اضافه کنید.
  4. برای شبیه‌سازی رفتار وابستگی، می‌توانید بااستفاده از روش‌های when() و thenReturn()، شرطی را مشخص کنید و مقدار بازگشتی را درصورت برآورده شدن شرط تعیین کنید.

مثال زیر نشان می‌دهد که چگونه می‌توانید یک آزمون واحد ایجاد کنید که از یک شیء ساختگی Context در Kotlin که با Mockito-Kotlin ایجاد شده است، استفاده می‌کند.

import android.content.Context
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.junit.MockitoJUnitRunner
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock

private const val FAKE_STRING = "HELLO WORLD"

@RunWith(MockitoJUnitRunner::class)
class MockedContextTest {

  @Mock
  private lateinit var mockContext: Context

  @Test
  fun readStringFromContext_LocalizedString() {
    // Given a mocked Context injected into the object under test...
    val mockContext = mock<Context> {
        on { getString(R.string.name_label) } doReturn FAKE_STRING
    }

    val myObjectUnderTest = ClassUnderTest(mockContext)

    // ...when the string is returned from the object under test...
    val result: String = myObjectUnderTest.getName()

    // ...then the result should be the expected one.
    assertEquals(result, FAKE_STRING)
  }
}

برای کسب اطلاعات بیشتر درباره استفاده از چارچوب Mockito، به مرجع میانای برنامه‌سازی کاربردی Mockito و کلاس SharedPreferencesHelperTest در کد نمونه مراجعه کنید. Codelab آزمایش Android را هم امتحان کنید.

خطا: «Method ... not mocked»

اگر بخواهید با پیام Error: "Method ... not mocked به هرکدام از روش‌های کتابخانه Mockable Android دسترسی پیدا کنید، این کتابخانه استثنایی را ایجاد می‌کند.

اگر استثناهای ایجادشده برای آزمایش‌هایتان مشکل‌ساز است، می‌توانید رفتار را تغییر دهید تا روش‌ها به‌جای آن، بسته به نوع پاسخ، null یا صفر برگردانند. برای انجام این کار، پیکربندی زیر را در فایل build.gradle سطح بالای پروژه‌تان در Groovy اضافه کنید:

android {
  ...
  testOptions {
    unitTests.returnDefaultValues = true
  }