برنامه نویسی

شروع با تست اندروید: ساخت برنامه های قابل اعتماد با اطمینان

آموزش اصول اولیه تست اندروید، یک مرحله در یک زمان (قسمت 2/3)

مقاله قبلی


مخاطبین هدف این وبلاگ

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


تست رابط کاربری

تست UI معمولاً به آزمایش رابط کاربری با شبیه سازی عملکرد کاربر و تأیید رفتار عناصر UI اشاره دارد.

چارچوب های تست UI معروف

چارچوب توضیحات
اسپرسو چارچوب تست رابط کاربری اندروید برای انجام تعامل با رابط کاربری و اظهار حالت. (تست جعبه سفید)
UI Automator برای انجام آزمایش UI کاربردی بین برنامه‌ای در سیستم و برنامه‌های نصب‌شده. (تست جعبه سیاه و جعبه سفید)
تست UI Junit را بنویسید برای ارائه قوانین Junit، تابع composable را در Junit فراخوانی کنید. همچنین API هایی را برای انجام تعامل UI و اظهار حالت ارائه می دهد.

نوشتن رابط کاربری + تست واحد تعامل

چارچوب تست Compose UI به شما امکان می‌دهد تأیید کنید که رفتار کد Compose شما همانطور که انتظار می‌رود کار می‌کند. فراهم می کند
مجموعه ای از API های آزمایشی که به شما کمک می کند عناصر UI را پیدا کنید، ویژگی های آنها را بررسی کنید و اقدامات کاربر را انجام دهید. با استفاده از این APIها، می‌توانید محتوای قابل ترکیب را نصب کنید و رفتارهای مورد انتظار را اثبات کنید.

این androidx.compose.ui.test.junit4 ماژول شامل a ComposeTestRule و یک پیاده سازی برای اندروید به نام AndroidComposeTestRule. از طریق این قانون می توانید Compose content را تنظیم کنید یا به فعالیت دسترسی داشته باشید. شما قوانین را با استفاده از توابع کارخانه می سازید createComposeRule یا اگر نیاز به دسترسی به یک فعالیت دارید، createAndroidComposeRule.

برای Compose UI Unit Tests، می توانید از RobolectricTestRunner، یک JUnit Test Runner که کد تست را مستقیماً روی آن اجرا می کند JVM. این امر نیاز به یک دستگاه اندروید فیزیکی یا مجازی را از بین می برد، به طور قابل توجهی سرعت اجرای آزمایش را افزایش می دهد، از نتایج ثابت اطمینان حاصل می کند و فرآیند آزمایش را ساده می کند.

با این حال، برخی از کلاس ها و روش ها از android.jar برای عملکرد صحیح نیاز به پیکربندی اضافی دارد. به عنوان مثال، دسترسی به منابع Android یا استفاده از روش‌هایی مانند Log ممکن است برای بازگرداندن مقادیر پیش‌فرض یا مسخره‌شده به تنظیمات نیاز داشته باشد. لطفاً برای پیکربندی لازم به بخش تنظیمات زیر مراجعه کنید.


نوشتن رابط کاربری + تست واحد تعامل

در این تست، ما در حال بررسی رفتار آن هستیم وارد شوید صفحه نمایش قابل ترکیب با اطمینان از اینکه دکمه ورود به سیستم است
تنها زمانی فعال می شود که ورودی های ارائه شده توسط کاربر معتبر باشد.

  1. اعتبار سنجی حالت اولیه: آزمایش تأیید می کند که دکمه ورود در ابتدا زمانی که هیچ ورودی ارائه نمی شود غیرفعال است.

  2. اعتبار سنجی ورودی جزئی: این تست وارد کردن ترکیبات ایمیل و رمز عبور نامعتبر را گام به گام شبیه‌سازی می‌کند تا اطمینان حاصل شود که دکمه تا زمانی که تمام شرایط اعتبار برآورده نشود، غیرفعال می‌ماند.

  3. اعتبار سنجی ورودی معتبر: در نهایت، آزمون تأیید می‌کند که دکمه ورود تنها زمانی فعال می‌شود که ایمیل و رمز عبور معیارهای اعتبارسنجی مورد نیاز (فرمت ایمیل معتبر و رمز عبور با طول کافی) را داشته باشند.

این تست تضمین می کند که وارد شوید composable به درستی اعتبار ورودی را اعمال می کند و دکمه ورود را فقط در شرایط معتبر فعال می کند.

سیستم در حال تست

@Composable
fun Login(onSuccess: (email: Email) -> Unit, viewModel: LoginViewModel = hiltViewModel()) {

  LaunchedEffect(key1 = viewModel.loginState, block = {
    if (viewModel.loginState == LoginState.LoginSuccess) onSuccess(viewModel.email)
  })

  Column {
    Text(text = stringResource(id = R.string.login))

    EmailInput(modifier = Modifier
      .semantics { testTagsAsResourceId = true;testTag = "emailInput" }
      .testTag("emailInput")
      .fillMaxWidth(),
      value = viewModel.email.value ?: "",
      isEnabled = viewModel.loginState !== LoginState.InProgress,
      onValueChange = viewModel::updateEmail)

    PasswordInput(modifier = Modifier
      .semantics { testTagsAsResourceId = true;testTag = "passwordInput" }
      .fillMaxWidth(),
      value = viewModel.password.value ?: "",
      isEnabled = viewModel.loginState !== LoginState.InProgress,
      onValueChange = viewModel::updatePassword)

    if (viewModel.loginState === LoginState.LoginPending){
        PrimaryButton(modifier = Modifier
            .semantics { testTagsAsResourceId = true;testTag = "loginButton" }
            .fillMaxWidth(),
            text = stringResource(id = R.string.login),
            enabled = viewModel.isLoginButtonEnabled,
            onClick = viewModel::login)
    }

    if (viewModel.loginState === LoginState.InProgress){
        CircularProgressIndicator(
            modifier = Modifier
                .semantics { testTagsAsResourceId = true;testTag = "progressLoader" }
                .align(Alignment.CenterHorizontally)
        )
    }
  }
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

تست کنید

@RunWith(RobolectricTestRunner::class)
class LoginKtTest {

  @get:Rule
  val composeRule = createComposeRule()

  @get:Rule
  var mainCoroutineRule = MainCoroutineRule()

  @Test
  fun shouldEnableButtonOnlyWhenInputsAreValid() {
    with(composeRule) {
      val loginUseCase = mockk()
      val loginViewModel = LoginViewModel(loginUseCase)
      setContent { Login(onSuccess = {}, viewModel = loginViewModel) }
      onNodeWithTag("loginButton").assertIsNotEnabled()

      onNodeWithTag("emailInput").performTextInput("abcd")
      onNodeWithTag("loginButton").assertIsNotEnabled()

      onNodeWithTag("emailInput").performTextInput("abcd@gmail.com")
      onNodeWithTag("loginButton").assertIsNotEnabled()

      onNodeWithTag("passwordInput").performTextInput("12")
      onNodeWithTag("loginButton").assertIsNotEnabled()

      onNodeWithTag("passwordInput").performTextInput("12345")
      onNodeWithTag("loginButton").assertIsEnabled()
    }
  }
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید


وابستگی ها

// Allows us to create and configure mock objects, stub methods, verify method invocations, and more
androidTestImplementation("io.mockk:mockk-agent:1.13.5")
androidTestImplementation("io.mockk:mockk-android:1.13.5")
androidTestImplementation("org.mockito.kotlin:mockito-kotlin:5.4.0")

// Assertion library
androidTestImplementation("com.google.truth:truth:1.1.4")

// Needed for createComposeRule , createAndroidComposeRule and other rules used to perform UI test
testImplementation("androidx.compose.ui:ui-test-junit4:$compose_version") // used with robolectric to run ui test on jvm
androidTestImplementation("androidx.compose.ui:ui-test-junit4:$compose_version") // used with AndroidTestRunner to run ui test on virtual/physical device.

// Needed for createComposeRule(), but not for createAndroidComposeRule():
debugImplementation("androidx.compose.ui:ui-test-manifest:$compose_version")

// Dependency injection for For instrumented tests on JVM
testImplementation("com.google.dagger:hilt-android-testing:2.49")
kaptTest("com.google.dagger:hilt-compiler:2.49")

// Needed to run android UI test on JVM instead of on an emulator or device
testImplementation("org.robolectric:robolectric:4.10.3)

// Helper for other arch dependencies, including JUnit test rules that can be used with LiveData, coroutines etc
testImplementation("androidx.arch.core:core-testing:2.2.0")
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

راه اندازی

testOptions {
        unitTests {
            // Enables unit tests to use Android resources, assets, and manifests.
            isIncludeAndroidResources = true
            // Whether unmocked methods from android.jar should throw exceptions or return default values (i.e. zero or null).
            isReturnDefaultValues = true
        }
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

فرمان

./gradlew testDebugUnitTest
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

کد منبع


کد خود را آزمایش کنید، به نگرانی های خود استراحت دهید

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

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا