آموزش 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() {
}
اصول تستنویسی حرفهای
- هر تست فقط یک چیز را تست کند
- تستها مستقل باشند
- تستها سریع اجرا شوند
- از Mock استفاده کنید
- نام تستها واضح باشد
قانون 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
- کاهش Bug
- افزایش کیفیت پروژه
- Refactor ایمن
- توسعه سریعتر
- اعتماد بیشتر به کد
اشتباهات رایج
- تست نکردن Edge Case ها
- وابستگی به اینترنت واقعی
- تستهای کند
- تست چند رفتار در یک 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 یکی از مهمترین مهارتهای برنامهنویسان حرفهای اندروید است و نقش مهمی در ساخت پروژههای پایدار و قابل اعتماد دارد.