برنامه‌نویسی اندروید

آموزش unit Test در اندروید

آموزش unit Test در اندروید

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

یکی از مهم‌ترین انواع تست در توسعه اندروید:

Unit Test

است.

Unit Test به شما کمک می‌کند بخش‌های مختلف برنامه را به‌صورت مستقل تست کنید و مطمئن شوید منطق برنامه درست کار می‌کند.

در این مقاله به‌صورت کامل و گام‌به‌گام با Unit Testing در اندروید آشنا می‌شویم.

Unit Test چیست؟

Unit Test یعنی تست کردن کوچک‌ترین بخش‌های برنامه به‌صورت مستقل.

مثلاً:

  • یک تابع
  • یک کلاس
  • یک Repository
  • یک UseCase
  • یک ViewModel

هدف Unit Test

هدف اصلی Unit Test:

  • جلوگیری از Bug
  • افزایش کیفیت کد
  • اطمینان از عملکرد صحیح
  • ساده‌تر شدن Refactor
  • توسعه پایدار پروژه

است.

تفاوت Unit Test و UI Test

Unit Test

فقط منطق برنامه را تست می‌کند.

مثال:

جمع دو عدد
اعتبارسنجی ایمیل
دریافت داده Repository

UI Test

رابط کاربری را تست می‌کند.

مثال:

کلیک روی دکمه
اسکرول لیست
باز شدن صفحه

چرا Unit Test در اندروید مهم است؟

در پروژه‌های اندرویدی معمولاً:

  • API زیاد داریم
  • State های مختلف داریم
  • منطق پیچیده داریم
  • توسعه تیمی انجام می‌شود

Unit Test کمک می‌کند تغییرات جدید باعث خراب شدن بخش‌های قبلی نشوند.

کتابخانه‌های تست در اندروید

مهم‌ترین ابزارهای تست:

  • JUnit
  • Mockito
  • MockK
  • Truth
  • Turbine
  • Robolectric

JUnit چیست؟

JUnit محبوب‌ترین فریمورک تست برای Java و Kotlin است.

افزودن Dependency های تست

build.gradle

dependencies {

    testImplementation "junit:junit:4.13.2"

    testImplementation "org.mockito:mockito-core:5.12.0"

    testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:1.8.1"
}

ساخت اولین Unit Test

فرض کنید تابع زیر را داریم:

class Calculator {

    fun sum(
        a: Int,
        b: Int
    ): Int {

        return a + b
    }
}

نوشتن تست

class CalculatorTest {

    @Test
    fun testSum() {

        val calculator =
            Calculator()

        val result =
            calculator.sum(2, 3)

        assertEquals(5, result)
    }
}

توضیح بخش‌های تست

@Test

@Test

مشخص می‌کند این تابع یک Test است.

assertEquals

assertEquals(expected, actual)

نتیجه مورد انتظار را بررسی می‌کند.

اجرای تست

داخل Android Studio:

Right Click
→ Run Test

ساخت Validator و تست آن

Validator

class EmailValidator {

    fun isValid(
        email: String
    ): Boolean {

        return email.contains("@")
    }
}

Unit Test

class EmailValidatorTest {

    @Test
    fun email_is_valid() {

        val validator =
            EmailValidator()

        val result =
            validator.isValid(
                "test@gmail.com"
            )

        assertEquals(true, result)
    }

    @Test
    fun email_is_invalid() {

        val validator =
            EmailValidator()

        val result =
            validator.isValid(
                "testgmail.com"
            )

        assertEquals(false, result)
    }
}

نام‌گذاری تست‌ها

بهتر است نام تست‌ها واضح باشند.

مثال:

email_is_valid
user_list_should_not_be_empty
login_should_return_success

تست ViewModel در اندروید

یکی از مهم‌ترین بخش‌ها در معماری MVVM تست ViewModel است.

نمونه ViewModel

class CounterViewModel
    : ViewModel() {

    private val _count =
        MutableStateFlow(0)

    val count = _count

    fun increment() {

        _count.value++
    }
}

تست ViewModel

class CounterViewModelTest {

    @Test
    fun increment_should_increase_count() {

        val viewModel =
            CounterViewModel()

        viewModel.increment()

        assertEquals(
            1,
            viewModel.count.value
        )
    }
}

Mock چیست؟

گاهی کلاس ما وابسته به:

  • API
  • Database
  • Repository

است.

در تست نباید از منابع واقعی استفاده کنیم.

برای این کار از:

Mock

استفاده می‌شود.

Mockito چیست؟

Mockito برای ساخت Mock استفاده می‌شود.

نمونه Repository

interface UserRepository {

    suspend fun getName(): String
}

ViewModel

class UserViewModel(
    private val repository:
    UserRepository
) : ViewModel() {

    suspend fun getName(): String {

        return repository.getName()
    }
}

تست با Mockito

class UserViewModelTest {

    private val repository =
        mock(UserRepository::class.java)

    @Test
    fun getName_should_return_name()
    = runTest {

        whenever(
            repository.getName()
        ).thenReturn("Ali")

        val viewModel =
            UserViewModel(repository)

        val result =
            viewModel.getName()

        assertEquals("Ali", result)
    }
}

تست Coroutines

برای تست Coroutine ها از:

runTest

استفاده می‌شود.

Dependency موردنیاز

testImplementation
"org.jetbrains.kotlinx:
kotlinx-coroutines-test:1.8.1"

تست Flow و StateFlow

مثال Flow

fun getNumbers() = flow {

    emit(1)

    emit(2)
}

تست Flow

@Test
fun flow_should_emit_values()
= runTest {

    val result =
        getNumbers().toList()

    assertEquals(
        listOf(1, 2),
        result
    )
}

تست Repository

Repository

class UserRepository {

    fun getUsers(): List<String> {

        return listOf(
            "Ali",
            "Sara"
        )
    }
}

Unit Test

class UserRepositoryTest {

    @Test
    fun getUsers_should_return_list() {

        val repository =
            UserRepository()

        val result =
            repository.getUsers()

        assertEquals(
            2,
            result.size
        )
    }
}

تست Exception

@Test(expected = Exception::class)
fun should_throw_exception() {

    throw Exception()
}

Before و After در تست

@Before

قبل از هر تست اجرا می‌شود.

@After

بعد از هر تست اجرا می‌شود.

مثال

@Before
fun setup() {

}

@After
fun cleanup() {

}

اصول تست‌نویسی حرفه‌ای

  1. هر تست فقط یک چیز را تست کند
  2. تست‌ها مستقل باشند
  3. تست‌ها سریع اجرا شوند
  4. از Mock استفاده کنید
  5. نام تست‌ها واضح باشد

قانون AAA در تست

ساختار استاندارد تست:

Arrange
Act
Assert

مثال AAA

@Test
fun test_sum() {

    // Arrange
    val calculator =
        Calculator()

    // Act
    val result =
        calculator.sum(1, 2)

    // Assert
    assertEquals(3, result)
}

تفاوت Fake و Mock

Fake

پیاده‌سازی ساده واقعی است.

Mock

رفتار شبیه‌سازی‌شده دارد.

مثال Fake Repository

class FakeRepository :
    UserRepository {

    override suspend fun
    getName(): String {

        return "Test"
    }
}

تست UseCase

UseCase

class SumUseCase {

    operator fun invoke(
        a: Int,
        b: Int
    ): Int {

        return a + b
    }
}

تست UseCase

@Test
fun usecase_should_return_sum() {

    val useCase =
        SumUseCase()

    val result =
        useCase(2, 5)

    assertEquals(7, result)
}

پوشه تست‌ها در اندروید

app/src/test/

برای Unit Test استفاده می‌شود.

Instrumentation Test چیست؟

تست‌هایی که روی دستگاه واقعی اجرا می‌شوند داخل:

androidTest/

قرار می‌گیرند.

تست در معماری Clean Architecture

در Clean Architecture معمولاً این بخش‌ها تست می‌شوند:

  • UseCase
  • Repository
  • Mapper
  • ViewModel
  • Validator

مزایای Unit Test

  1. کاهش Bug
  2. افزایش کیفیت پروژه
  3. Refactor ایمن
  4. توسعه سریع‌تر
  5. اعتماد بیشتر به کد

اشتباهات رایج

  1. تست نکردن Edge Case ها
  2. وابستگی به اینترنت واقعی
  3. تست‌های کند
  4. تست چند رفتار در یک Test

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

  • JUnit
  • Mockito
  • MockK
  • Robolectric

Unit Test در Jetpack Compose

در پروژه‌های مبتنی بر Jetpack Compose نیز تست اهمیت زیادی دارد.

معمولاً:

  • ViewModel
  • State
  • UseCase

تست می‌شوند.

جمع‌بندی

در این مقاله با Unit Testing در اندروید آشنا شدیم.

همچنین یاد گرفتیم:

  • Unit Test چیست
  • چگونه تست بنویسیم
  • تست ViewModel
  • تست Flow و Coroutine
  • استفاده از Mockito
  • Mock و Fake
  • تست Repository
  • اصول حرفه‌ای تست‌نویسی

Unit Test یکی از مهم‌ترین مهارت‌های برنامه‌نویسان حرفه‌ای اندروید است و نقش مهمی در ساخت پروژه‌های پایدار و قابل اعتماد دارد.

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

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