این کد لبه بخشی از دوره Advanced Android in Kotlin است. اگر از طریق کدها به ترتیب کار کنید، بیشترین ارزش را از این دوره خواهید گرفت، اما اجباری نیست. همه کدهای دوره در صفحه فرود Android Advanced in Kotlin Codelabs فهرست شده اند.
مقدمه
هنگامی که اولین ویژگی اولین برنامه خود را پیاده سازی کردید، احتمالاً کد را اجرا کرده اید تا تأیید کنید که مطابق انتظار کار می کند. شما یک تست انجام دادید، البته یک تست دستی . همانطور که به افزودن و بهروزرسانی ویژگیها ادامه میدهید، احتمالاً به اجرای کد و تأیید کارکرد آن نیز ادامه دادهاید. اما هر بار انجام این کار به صورت دستی خسته کننده است، مستعد اشتباه است و مقیاس نمی شود.
کامپیوترها در مقیاس بندی و اتوماسیون عالی هستند! بنابراین، توسعهدهندگان شرکتهای بزرگ و کوچک ، تستهای خودکار را مینویسند، این تستها آزمایشهایی هستند که توسط نرمافزار اجرا میشوند و نیازی به اجرای دستی برنامه برای تأیید کارکرد کد ندارند.
چیزی که در این سری از نرم افزارهای کد یاد خواهید گرفت، نحوه ایجاد مجموعه ای از تست ها (معروف به مجموعه تست) برای یک برنامه واقعی است.
این اولین کد لبه مبانی تست در اندروید را پوشش می دهد، شما اولین تست های خود را می نویسید و یاد می گیرید که چگونه LiveData
و ViewModel
s را آزمایش کنید.
آنچه از قبل باید بدانید
باید با:
- زبان برنامه نویسی کاتلین
- کتابخانههای اصلی Android Jetpack:
ViewModel
وLiveData
- معماری برنامه، از الگوی راهنمای معماری اپلیکیشن و کد لبه های Android Fundamentals پیروی می کند
چیزی که یاد خواهید گرفت
با موضوعات زیر آشنا خواهید شد:
- نحوه نوشتن و اجرای تست های واحد در اندروید
- نحوه استفاده از Test Driven Development
- نحوه انتخاب تست های ابزاردار و تست های محلی
شما با کتابخانه ها و مفاهیم کد زیر آشنا خواهید شد:
کاری که خواهی کرد
- تست های محلی و ابزاری را در اندروید تنظیم، اجرا و تفسیر کنید.
- تست های واحد را در اندروید با استفاده از JUnit4 و Hamcrest بنویسید.
- تست های ساده
LiveData
وViewModel
را بنویسید.
در این سری از کدها، شما با برنامه TO-DO Notes کار خواهید کرد. این برنامه به شما امکان می دهد وظایف را برای تکمیل بنویسید و آنها را در یک لیست نمایش دهید. سپس میتوانید آنها را بهعنوان تکمیلشده یا خیر علامتگذاری کنید، فیلتر کنید یا حذف کنید.
این برنامه به زبان Kotlin نوشته شده است، دارای چندین صفحه نمایش است، از اجزای Jetpack استفاده می کند و معماری را از یک راهنما تا معماری برنامه دنبال می کند. با یادگیری نحوه آزمایش این برنامه، می توانید برنامه هایی را که از کتابخانه ها و معماری مشابهی استفاده می کنند، آزمایش کنید.
برای شروع، کد را دانلود کنید:
از طرف دیگر، می توانید مخزن Github را برای کد کلون کنید:
$ git clone https://github.com/googlecodelabs/android-testing.git $ cd android-testing $ git checkout starter_code
در این کار، برنامه را اجرا کرده و پایه کد را کاوش خواهید کرد.
مرحله 1: برنامه نمونه را اجرا کنید
هنگامی که برنامه TO-DO را دانلود کردید، آن را در Android Studio باز کرده و اجرا کنید. باید کامپایل شود. با انجام موارد زیر برنامه را کاوش کنید:
- با دکمه اکشن شناور پلاس یک کار جدید ایجاد کنید. ابتدا عنوانی را وارد کنید، سپس اطلاعات تکمیلی مربوط به کار را وارد کنید. آن را با چک سبز FAB ذخیره کنید.
- در لیست کارها، روی عنوان کاری که به تازگی تکمیل کردید کلیک کنید و به صفحه جزئیات آن کار نگاه کنید تا بقیه توضیحات را ببینید.
- در فهرست یا در صفحه جزئیات، کادر تأیید آن کار را علامت بزنید تا وضعیت آن را روی «تکمیل» تنظیم کنید.
- به صفحه وظایف برگردید، منوی فیلتر را باز کنید و وظایف را بر اساس وضعیت فعال و تکمیل شده فیلتر کنید.
- کشوی پیمایش را باز کنید و روی Statistics کلیک کنید.
- به صفحه نمای کلی بازگشته و از منوی کشوی پیمایش، پاک کردن تکمیل شده را انتخاب کنید تا همه وظایف با وضعیت تکمیل شده حذف شوند.
مرحله 2: نمونه کد برنامه را کاوش کنید
برنامه TO-DO مبتنی بر نمونه آزمایشی و معماری محبوب طرحهای معماری (با استفاده از نسخه معماری واکنشی نمونه) است. این برنامه از معماری یک راهنما به معماری برنامه پیروی می کند. از ViewModels با Fragments، Repository و Room استفاده می کند. اگر با هر یک از نمونه های زیر آشنا هستید، این برنامه معماری مشابهی دارد:
- اتاق با نمای Codelab
- کد لبه های آموزشی Android Kotlin Fundamentals
- کد لبه های آموزش پیشرفته اندروید
- نمونه گل آفتابگردان اندروید
- دوره آموزشی توسعه اپلیکیشن های اندروید با Kotlin Udacity
مهمتر است که معماری کلی برنامه را درک کنید تا اینکه درک عمیقی از منطق در هر لایه داشته باشید.
در اینجا خلاصه بسته هایی است که خواهید یافت:
بسته: | |
| صفحه افزودن یا ویرایش یک کار: کد لایه رابط کاربری برای افزودن یا ویرایش یک کار. |
| لایه داده: این لایه با لایه داده وظایف سروکار دارد. این شامل پایگاه داده، شبکه و کد مخزن است. |
| صفحه آمار: کد لایه رابط کاربری برای صفحه آمار. |
| صفحه جزئیات کار: کد لایه رابط کاربری برای یک کار واحد. |
| صفحه وظایف: کد لایه رابط کاربری برای لیست تمام وظایف. |
| کلاسهای کاربردی: کلاسهای مشترک مورد استفاده در بخشهای مختلف برنامه، بهعنوان مثال برای طرحبندی بازخوانی کشیدن انگشت که در چندین صفحه استفاده میشود. |
لایه داده (.data)
این برنامه شامل یک لایه شبکه شبیه سازی شده، در بسته راه دور ، و یک لایه پایگاه داده، در بسته محلی است. برای سادگی، در این پروژه لایه شبکه تنها با یک HashMap
با تاخیر شبیه سازی می شود تا درخواست های واقعی شبکه.
DefaultTasksRepository
بین لایه شبکه و لایه پایگاه داده مختصات یا واسطه می شود و همان چیزی است که داده ها را به لایه UI برمی گرداند.
لایه رابط کاربری (.addedittask، .statistics،.taskdetail، .tasks)
هر یک از بسته های لایه UI شامل یک قطعه و یک مدل view به همراه هر کلاس دیگری است که برای UI مورد نیاز است (مانند یک آداپتور برای لیست وظایف). TaskActivity
اکتیویتی است که شامل تمام قطعات است.
ناوبری
ناوبری برای برنامه توسط مؤلفه ناوبری کنترل می شود. در فایل nav_graph.xml
تعریف شده است. ناوبری در مدل های view با استفاده از کلاس Event
فعال می شود. مدلهای view نیز تعیین میکنند که چه آرگومانهایی باید منتقل شوند. قطعات Event
را مشاهده میکنند و پیمایش واقعی بین صفحهها را انجام میدهند.
در این کار، اولین تست های خود را اجرا می کنید.
- در Android Studio، پنجره Project را باز کنید و این سه پوشه را پیدا کنید:
-
com.example.android.architecture.blueprints.todoapp
-
com.example.android.architecture.blueprints.todoapp (androidTest)
-
com.example.android.architecture.blueprints.todoapp (test)
این پوشه ها به عنوان مجموعه منبع شناخته می شوند. مجموعههای منبع، پوشههایی هستند که حاوی کد منبع برنامه شما هستند. مجموعه های منبع، که سبز رنگ هستند ( آندرویدتست و تست ) حاوی تست های شما هستند. هنگامی که یک پروژه اندروید جدید ایجاد می کنید، به طور پیش فرض سه مجموعه منبع زیر را دریافت می کنید. آنها عبارتند از:
-
main
: حاوی کد برنامه شما است. این کد در بین تمام نسخههای مختلف برنامهای که میتوانید بسازید به اشتراک گذاشته شده است (معروف به انواع ساخت ) -
androidTest
: شامل تست هایی است که به عنوان تست های ابزاری شناخته می شوند. -
test
: شامل تست هایی است که به عنوان تست های محلی شناخته می شوند.
تفاوت بین تست های محلی و تست های ابزار دقیق در نحوه اجرا آنهاست.
تست های محلی ( مجموعه منبع test
)
این تست ها به صورت محلی روی JVM ماشین توسعه شما اجرا می شوند و نیازی به شبیه ساز یا دستگاه فیزیکی ندارند. به همین دلیل، آنها سریع می دوند، اما وفاداری آنها کمتر است، به این معنی که کمتر مانند دنیای واقعی عمل می کنند.
در Android Studio تست های محلی با یک نماد مثلث سبز و قرمز نشان داده می شوند.
تست های ابزاری ( مجموعه منبع androidTest
)
این تستها بر روی دستگاههای اندرویدی واقعی یا شبیهسازی شده اجرا میشوند، بنابراین آنچه را که در دنیای واقعی اتفاق میافتد منعکس میکنند، اما همچنین بسیار کندتر هستند.
در اندروید استودیو، تستهای ابزاردار با یک اندروید با نماد مثلث سبز و قرمز نشان داده میشوند.
مرحله 1: یک تست محلی را اجرا کنید
- پوشه
test
را باز کنید تا فایل ExampleUnitTest.kt را پیدا کنید. - روی آن کلیک راست کرده و Run ExampleUnitTest را انتخاب کنید.
شما باید خروجی زیر را در پنجره Run در پایین صفحه مشاهده کنید:
- به علامتهای سبز رنگ توجه کنید و نتایج آزمایش را گسترش دهید تا تأیید کنید که آزمونی به نام
addition_isCorrect
رد شده است. خوب است بدانید که اضافه کردن همانطور که انتظار می رود کار می کند!
مرحله 2: آزمون را با شکست مواجه کنید
در زیر آزمونی است که شما انجام دادید.
ExampleUnitTest.kt
// A test class is just a normal class
class ExampleUnitTest {
// Each test is annotated with @Test (this is a Junit annotation)
@Test
fun addition_isCorrect() {
// Here you are checking that 4 is the same as 2+2
assertEquals(4, 2 + 2)
}
}
توجه کنید که تست ها
- یک کلاس در یکی از مجموعه های منبع تست هستند.
- شامل توابعی است که با حاشیه نویسی
@Test
شروع می شود (هر تابع یک تست واحد است). - u8 معمولاً حاوی عبارات ادعایی است.
اندروید از کتابخانه آزمایشی JUnit برای تست استفاده می کند (در این نرم افزار JUnit4). هر دو، ادعاها و حاشیه نویسی @Test
از JUnit می آیند.
یک ادعا هسته اصلی آزمون شما است. این یک بیانیه کد است که بررسی می کند کد یا برنامه شما مطابق انتظار عمل کرده است. در این مورد، ادعا assertEquals(4, 2 + 2)
است که بررسی می کند که 4 برابر با 2 + 2 است.
برای اینکه ببینید یک آزمون ناموفق چگونه به نظر می رسد، یک ادعا اضافه کنید که به راحتی می توانید ببینید که باید شکست بخورد. بررسی می کند که 3 برابر با 1+1 باشد.
-
assertEquals(3, 1 + 1)
را به آزمونaddition_isCorrect
اضافه کنید.
ExampleUnitTest.kt
class ExampleUnitTest {
// Each test is annotated with @Test (this is a Junit annotation)
@Test
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
assertEquals(3, 1 + 1) // This should fail
}
}
- تست را اجرا کنید.
- در نتایج آزمون، به یک X در کنار آزمون توجه کنید.
- همچنین توجه کنید:
- یک ادعای ناموفق در کل آزمون مردود است.
- مقدار مورد انتظار (3) در مقابل مقداری که واقعاً محاسبه شده است (2) به شما گفته می شود.
- شما به خط ادعای شکست خورده هدایت می شوید
(ExampleUnitTest.kt:16)
.
مرحله 3: یک تست ابزاری را اجرا کنید
تستهای ابزاری در مجموعه منبع androidTest
هستند.
- مجموعه منبع
androidTest
را باز کنید. - تستی به نام
ExampleInstrumentedTest
را اجرا کنید.
ExampleInstrumentedTest
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.example.android.architecture.blueprints.reactive",
appContext.packageName)
}
}
برخلاف آزمایش محلی، این آزمایش روی دستگاهی اجرا میشود (در مثال زیر یک تلفن شبیهسازیشده Pixel 2):
اگر دستگاهی متصل دارید یا شبیه ساز در حال اجرا است، باید اجرای آزمایشی را روی شبیه ساز ببینید.
در این کار، تستهایی را برای getActiveAndCompleteStats
مینویسید، که درصد آمار کار فعال و کامل را برای برنامه شما محاسبه میکند. می توانید این اعداد را در صفحه آمار اپلیکیشن مشاهده کنید.
مرحله 1: یک کلاس آزمایشی ایجاد کنید
- در مجموعه منبع
main
، درtodoapp.statistics
،StatisticsUtils.kt
باز کنید. - تابع
getActiveAndCompletedStats
پیدا کنید.
StatisticsUtils.kt
internal fun getActiveAndCompletedStats(tasks: List<Task>?): StatsResult {
val totalTasks = tasks!!.size
val numberOfActiveTasks = tasks.count { it.isActive }
val activePercent = 100 * numberOfActiveTasks / totalTasks
val completePercent = 100 * (totalTasks - numberOfActiveTasks) / totalTasks
return StatsResult(
activeTasksPercent = activePercent.toFloat(),
completedTasksPercent = completePercent.toFloat()
)
}
data class StatsResult(val activeTasksPercent: Float, val completedTasksPercent: Float)
تابع getActiveAndCompletedStats
لیستی از وظایف را می پذیرد و یک StatsResult
برمی گرداند. StatsResult
یک کلاس داده است که شامل دو عدد است، درصد کارهای تکمیل شده و درصد فعال .
اندروید استودیو ابزارهایی برای تولید خرده آزمایشی به شما می دهد و به شما کمک می کند تا تست های این تابع را پیاده سازی کنید.
- روی
getActiveAndCompletedStats
کلیک راست کرده و Generate > Test را انتخاب کنید.
گفتگوی Create Test باز می شود:
- نام کلاس: را به
StatisticsUtilsTest
تغییر دهید (به جایStatisticsUtilsKtTest
؛ نداشتن KT در نام کلاس تست کمی بهتر است). - بقیه موارد پیش فرض را حفظ کنید. JUnit 4 کتابخانه آزمایشی مناسبی است. بسته مقصد درست است (موقعیت کلاس
StatisticsUtils
را منعکس میکند) و نیازی به علامت زدن هیچ یک از کادرها نیست (این فقط کد اضافی ایجاد میکند، اما شما تست خود را از ابتدا مینویسید). - OK را فشار دهید
گفتگوی Choose Destination Directory باز می شود:
شما یک تست محلی انجام می دهید زیرا تابع شما محاسبات ریاضی را انجام می دهد و شامل کد خاصی برای اندروید نمی شود. بنابراین، نیازی به اجرای آن بر روی یک دستگاه واقعی یا شبیه سازی شده نیست.
- دایرکتوری
test
(نهandroidTest
) را انتخاب کنید زیرا در حال نوشتن تست های محلی هستید. - روی OK کلیک کنید.
- به کلاس
StatisticsUtilsTest
ایجاد شده درtest/statistics/
توجه کنید.
مرحله 2: اولین تابع تست خود را بنویسید
شما یک تست می نویسید که بررسی می کند:
- اگر هیچ کار تکمیل شده و یک کار فعال وجود ندارد،
- که درصد تست های فعال 100% است
- و درصد کارهای انجام شده 0 درصد است.
-
StatisticsUtilsTest
باز کنید. - تابعی به نام
getActiveAndCompletedStats_noCompleted_returnsHundredZero
ایجاد کنید.
StatisticsUtilsTest.kt
class StatisticsUtilsTest {
fun getActiveAndCompletedStats_noCompleted_returnsHundredZero() {
// Create an active task
// Call your function
// Check the result
}
}
- حاشیهنویسی
@Test
را در بالای نام تابع اضافه کنید تا نشان دهید که یک آزمایش است. - فهرستی از وظایف ایجاد کنید.
// Create an active task
val tasks = listOf<Task>(
Task("title", "desc", isCompleted = false)
)
- با این کارها با
getActiveAndCompletedStats
تماس بگیرید.
// Call your function
val result = getActiveAndCompletedStats(tasks)
- با استفاده از ادعاها بررسی کنید که
result
همان چیزی است که انتظار داشتید.
// Check the result
assertEquals(result.completedTasksPercent, 0f)
assertEquals(result.activeTasksPercent, 100f)
در اینجا کد کامل است.
StatisticsUtilsTest.kt
class StatisticsUtilsTest {
@Test
fun getActiveAndCompletedStats_noCompleted_returnsHundredZero() {
// Create an active task (the false makes this active)
val tasks = listOf<Task>(
Task("title", "desc", isCompleted = false)
)
// Call your function
val result = getActiveAndCompletedStats(tasks)
// Check the result
assertEquals(result.completedTasksPercent, 0f)
assertEquals(result.activeTasksPercent, 100f)
}
}
- تست را اجرا کنید (راست کلیک کنید
StatisticsUtilsTest
و Run را انتخاب کنید).
باید بگذرد:
مرحله 3: وابستگی Hamcrest را اضافه کنید
از آنجایی که تست های شما به عنوان مستندی از آنچه کد شما انجام می دهد عمل می کند، زمانی که قابل خواندن توسط انسان باشد بسیار خوب است. دو ادعای زیر را با هم مقایسه کنید:
assertEquals(result.completedTasksPercent, 0f)
// versus
assertThat(result.completedTasksPercent, `is`(0f))
ادعای دوم بسیار بیشتر شبیه یک جمله انسانی است. این با استفاده از یک چارچوب ادعایی به نام Hamcrest نوشته شده است. یکی دیگر از ابزارهای خوب برای نوشتن ادعاهای خواندنی، کتابخانه حقیقت است. شما از Hamcrest در این کد لبه برای نوشتن ادعاها استفاده خواهید کرد.
-
build.grade (Module: app)
را باز کنید و وابستگی زیر را اضافه کنید.
app/build.gradle
dependencies {
// Other dependencies
testImplementation "org.hamcrest:hamcrest-all:$hamcrestVersion"
}
معمولاً هنگام اضافه کردن یک وابستگی از implementation
استفاده می کنید، اما در اینجا از testImplementation
استفاده می کنید. هنگامی که آماده به اشتراک گذاری برنامه خود با جهان هستید، بهتر است اندازه APK خود را با هیچ یک از کدهای آزمایشی یا وابستگی های برنامه خود پر نکنید. با استفاده از تنظیمات gradle می توانید تعیین کنید که آیا کتابخانه باید در کد اصلی یا آزمایشی گنجانده شود. رایج ترین تنظیمات عبارتند از:
-
implementation
- وابستگی در همه مجموعه های منبع، از جمله مجموعه های منبع آزمایشی موجود است. -
testImplementation
- وابستگی فقط در مجموعه منبع آزمایشی موجود است. -
androidTestImplementation
— این وابستگی فقط در مجموعه منبعandroidTest
موجود است.
از کدام پیکربندی استفاده میکنید، محل استفاده از وابستگی را مشخص میکند. اگر بنویسید:
testImplementation "org.hamcrest:hamcrest-all:$hamcrestVersion"
این بدان معنی است که Hamcrest فقط در مجموعه منبع آزمایشی در دسترس خواهد بود. همچنین تضمین می کند که Hamcrest در برنامه نهایی شما قرار نخواهد گرفت.
مرحله 4: از Hamcrest برای نوشتن اظهارات استفاده کنید
- برای استفاده
assertThat
Hamcrest به جایassertEquals
، تستgetActiveAndCompletedStats_noCompleted_returnsHundredZero()
را به روز کنید.
// REPLACE
assertEquals(result.completedTasksPercent, 0f)
assertEquals(result.activeTasksPercent, 100f)
// WITH
assertThat(result.activeTasksPercent, `is`(100f))
assertThat(result.completedTasksPercent, `is`(0f))
توجه داشته باشید که در صورت درخواست می توانید از import import org.hamcrest.Matchers.`is`
استفاده کنید.
تست نهایی شبیه کد زیر خواهد بود.
StatisticsUtilsTest.kt
import com.example.android.architecture.blueprints.todoapp.data.Task
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.`is`
import org.junit.Test
class StatisticsUtilsTest {
@Test
fun getActiveAndCompletedStats_noCompleted_returnsHundredZero {
// Create an active tasks (the false makes this active)
val tasks = listOf<Task>(
Task("title", "desc", isCompleted = false)
)
// Call your function
val result = getActiveAndCompletedStats(tasks)
// Check the result
assertThat(result.activeTasksPercent, `is`(100f))
assertThat(result.completedTasksPercent, `is`(0f))
}
}
- تست به روز شده خود را اجرا کنید تا مطمئن شوید هنوز کار می کند!
این کد لبه همه چیزهای Hamcrest را به شما آموزش نمی دهد، بنابراین اگر می خواهید بیشتر بدانید، آموزش رسمی را ببینید.
این یک کار اختیاری برای تمرین است.
در این کار، تست های بیشتری را با استفاده از JUnit و Hamcrest می نویسید. همچنین با استفاده از یک استراتژی برگرفته از تمرین برنامه توسعه تست محور، تست ها را می نویسید. Test Driven Development یا TDD یک مکتب تفکر برنامه نویسی است که می گوید به جای اینکه ابتدا کد ویژگی خود را بنویسید، ابتدا تست های خود را بنویسید. سپس کد ویژگی خود را با هدف قبولی در آزمون های خود می نویسید.
مرحله 1. تست ها را بنویسید
تست هایی را برای زمانی که لیست وظایف معمولی دارید بنویسید:
- اگر یک کار تکمیل شده وجود دارد و هیچ کار فعالی وجود ندارد، درصد
activeTasks
باید0f
و درصد کارهای تکمیل شده باید100f
باشد. - اگر دو کار تکمیل شده و سه کار فعال وجود دارد، درصد تکمیل شده باید
40f
و درصد فعال باید60f
باشد.
مرحله 2. یک تست برای یک اشکال بنویسید
کد getActiveAndCompletedStats
همانطور که نوشته شده دارای یک اشکال است. توجه داشته باشید که چگونه آن را به درستی مدیریت نمی کند که چه اتفاقی می افتد اگر لیست خالی یا خالی باشد. در هر دو مورد، هر دو درصد باید صفر باشند.
internal fun getActiveAndCompletedStats(tasks: List<Task>?): StatsResult {
val totalTasks = tasks!!.size
val numberOfActiveTasks = tasks.count { it.isActive }
val activePercent = 100 * numberOfActiveTasks / totalTasks
val completePercent = 100 * (totalTasks - numberOfActiveTasks) / totalTasks
return StatsResult(
activeTasksPercent = activePercent.toFloat(),
completedTasksPercent = completePercent.toFloat()
)
}
برای رفع کد و نوشتن تست ها، از توسعه مبتنی بر تست استفاده خواهید کرد. توسعه تست محور این مراحل را دنبال می کند.
- تست را با استفاده از ساختار Given, When, Then و با نامی که از قرارداد پیروی می کند بنویسید.
- عدم موفقیت آزمون را تأیید کنید.
- حداقل کد را برای قبولی در آزمون بنویسید.
- برای همه تست ها تکرار کنید!
به جای اینکه با رفع اشکال شروع کنید، ابتدا با نوشتن تست ها شروع می کنید. سپس میتوانید تأیید کنید که آزمایشهایی دارید که از شما در برابر معرفی مجدد تصادفی این اشکالات در آینده محافظت میکند.
- اگر یک لیست خالی وجود داشته باشد (
emptyList()
)، هر دو درصد باید 0f باشند. - اگر در بارگیری وظایف خطایی وجود داشته باشد، لیست
null
خواهد بود و هر دو درصد باید 0f باشند. - تست های خود را اجرا کنید و تأیید کنید که آنها شکست خورده اند :
مرحله 3. رفع اشکال
اکنون که آزمایشات خود را دارید، باگ را برطرف کنید.
- رفع اشکال در
getActiveAndCompletedStats
با برگرداندن0f
در صورتnull
یا خالی بودنtasks
:
internal fun getActiveAndCompletedStats(tasks: List<Task>?): StatsResult {
return if (tasks == null || tasks.isEmpty()) {
StatsResult(0f, 0f)
} else {
val totalTasks = tasks.size
val numberOfActiveTasks = tasks.count { it.isActive }
StatsResult(
activeTasksPercent = 100f * numberOfActiveTasks / tasks.size,
completedTasksPercent = 100f * (totalTasks - numberOfActiveTasks) / tasks.size
)
}
}
- تست های خود را دوباره اجرا کنید و تأیید کنید که همه تست ها اکنون قبول شده اند!
با پیروی از TDD و نوشتن تستها در ابتدا، به این اطمینان کمک کردهاید که:
- عملکرد جدید همیشه دارای تست های مرتبط است. بنابراین آزمایشات شما به عنوان مستندی از آنچه کد شما انجام می دهد عمل می کند.
- آزمایشهای شما نتایج صحیح را بررسی میکنند و در برابر اشکالاتی که قبلاً دیدهاید محافظت میکنند.
راه حل: نوشتن تست های بیشتر
در اینجا تمام تست ها و کد ویژگی مربوطه آمده است.
StatisticsUtilsTest.kt
class StatisticsUtilsTest {
@Test
fun getActiveAndCompletedStats_noCompleted_returnsHundredZero {
val tasks = listOf(
Task("title", "desc", isCompleted = false)
)
// When the list of tasks is computed with an active task
val result = getActiveAndCompletedStats(tasks)
// Then the percentages are 100 and 0
assertThat(result.activeTasksPercent, `is`(100f))
assertThat(result.completedTasksPercent, `is`(0f))
}
@Test
fun getActiveAndCompletedStats_noActive_returnsZeroHundred() {
val tasks = listOf(
Task("title", "desc", isCompleted = true)
)
// When the list of tasks is computed with a completed task
val result = getActiveAndCompletedStats(tasks)
// Then the percentages are 0 and 100
assertThat(result.activeTasksPercent, `is`(0f))
assertThat(result.completedTasksPercent, `is`(100f))
}
@Test
fun getActiveAndCompletedStats_both_returnsFortySixty() {
// Given 3 completed tasks and 2 active tasks
val tasks = listOf(
Task("title", "desc", isCompleted = true),
Task("title", "desc", isCompleted = true),
Task("title", "desc", isCompleted = true),
Task("title", "desc", isCompleted = false),
Task("title", "desc", isCompleted = false)
)
// When the list of tasks is computed
val result = getActiveAndCompletedStats(tasks)
// Then the result is 40-60
assertThat(result.activeTasksPercent, `is`(40f))
assertThat(result.completedTasksPercent, `is`(60f))
}
@Test
fun getActiveAndCompletedStats_error_returnsZeros() {
// When there's an error loading stats
val result = getActiveAndCompletedStats(null)
// Both active and completed tasks are 0
assertThat(result.activeTasksPercent, `is`(0f))
assertThat(result.completedTasksPercent, `is`(0f))
}
@Test
fun getActiveAndCompletedStats_empty_returnsZeros() {
// When there are no tasks
val result = getActiveAndCompletedStats(emptyList())
// Both active and completed tasks are 0
assertThat(result.activeTasksPercent, `is`(0f))
assertThat(result.completedTasksPercent, `is`(0f))
}
}
StatisticsUtils.kt
internal fun getActiveAndCompletedStats(tasks: List<Task>?): StatsResult {
return if (tasks == null || tasks.isEmpty()) {
StatsResult(0f, 0f)
} else {
val totalTasks = tasks.size
val numberOfActiveTasks = tasks.count { it.isActive }
StatsResult(
activeTasksPercent = 100f * numberOfActiveTasks / tasks.size,
completedTasksPercent = 100f * (totalTasks - numberOfActiveTasks) / tasks.size
)
}
}
کار عالی با اصول رایتینگ و اجرای تست ها! در ادامه نحوه نوشتن تست های ViewModel
و LiveData
را یاد خواهید گرفت.
در بقیه بخش کد، یاد خواهید گرفت که چگونه برای دو کلاس اندروید تست بنویسید که در اکثر برنامه ها رایج است ViewModel
و LiveData
.
شما با نوشتن تست هایی برای TasksViewModel
شروع می کنید.
شما قرار است روی تست هایی تمرکز کنید که تمام منطق خود را در مدل view دارند و به کد مخزن متکی نیستند. کد مخزن شامل کدهای ناهمزمان، پایگاههای داده و تماسهای شبکه است که همگی پیچیدگی تست را اضافه میکنند. فعلاً از آن اجتناب میکنید و بر روی نوشتن تستهایی برای عملکرد ViewModel تمرکز میکنید که مستقیماً هیچ چیز را در مخزن آزمایش نمیکند.
تستی که می نویسید بررسی می کند که وقتی متد addNewTask
را فراخوانی می کنید، Event
برای باز کردن پنجره وظیفه جدید فعال شود. در اینجا کد برنامه ای است که می خواهید آزمایش کنید.
TasksViewModel.kt
fun addNewTask() {
_newTaskEvent.value = Event(Unit)
}
مرحله 1. یک کلاس TasksViewModelTest بسازید
با دنبال کردن همان مراحلی که برای StatisticsUtilTest
انجام دادید، در این مرحله، یک فایل آزمایشی برای TasksViewModelTest
ایجاد میکنید.
- کلاسی را که می خواهید آزمایش کنید، در بسته
tasks
،TasksViewModel.
- در کد، روی نام کلاس
TasksViewModel
-> Generate -> Test کلیک راست کنید.
- در صفحه Create Test ، برای پذیرش روی OK کلیک کنید (نیازی به تغییر هیچ یک از تنظیمات پیش فرض نیست).
- در گفتگوی Choose Destination Directory ، دایرکتوری آزمایشی را انتخاب کنید.
مرحله 2. شروع به نوشتن تست ViewModel خود کنید
در این مرحله شما یک تست مدل view اضافه میکنید تا آزمایش کنید که وقتی متد addNewTask
را فراخوانی میکنید، Event
برای باز کردن پنجره وظیفه جدید فعال میشود.
- یک آزمایش جدید به نام
addNewTask_setsNewTaskEvent
ایجاد کنید.
TasksViewModelTest.kt
class TasksViewModelTest {
@Test
fun addNewTask_setsNewTaskEvent() {
// Given a fresh TasksViewModel
// When adding a new task
// Then the new task event is triggered
}
}
در مورد زمینه برنامه چطور؟
هنگامی که نمونه ای از TasksViewModel
برای آزمایش ایجاد می کنید، سازنده آن به یک Application Context نیاز دارد. اما در این تست، شما یک برنامه کامل با فعالیتها و رابط کاربری و قطعات ایجاد نمیکنید، پس چگونه میتوانید زمینه برنامه را بدست آورید؟
TasksViewModelTest.kt
// Given a fresh ViewModel
val tasksViewModel = TasksViewModel(???)
کتابخانههای تست AndroidX شامل کلاسها و روشهایی هستند که نسخههایی از مؤلفههایی مانند برنامهها و فعالیتها را در اختیار شما قرار میدهند که برای آزمایشها در نظر گرفته شدهاند. هنگامی که یک آزمایش محلی دارید که در آن به کلاس های فریم ورک اندروید شبیه سازی شده نیاز دارید (مانند یک زمینه برنامه)، این مراحل را برای تنظیم صحیح AndroidX Test دنبال کنید:
- هسته تست AndroidX و وابستگی های ext را اضافه کنید
- وابستگی کتابخانه تست روبولکتریک را اضافه کنید
- کلاس را با اجرای تست AndroidJunit4 حاشیه نویسی کنید
- کد تست AndroidX را بنویسید
شما قرار است این مراحل را کامل کنید و سپس بفهمید که آنها با هم چه می کنند.
مرحله 3. وابستگی های gradle را اضافه کنید
- این وابستگی ها را در فایل
build.gradle
ماژول برنامه خود کپی کنید تا وابستگی های هسته اصلی AndroidX Test و ext و همچنین وابستگی تست Robolectric را اضافه کنید.
app/build.gradle
// AndroidX Test - JVM testing
testImplementation "androidx.test.ext:junit-ktx:$androidXTestExtKotlinRunnerVersion"
testImplementation "androidx.test:core-ktx:$androidXTestCoreVersion"
testImplementation "org.robolectric:robolectric:$robolectricVersion"
مرحله 4. JUnit Test Runner را اضافه کنید
-
@RunWith(AndroidJUnit4::class)
بالای کلاس آزمایشی خود اضافه کنید.
TasksViewModelTest.kt
@RunWith(AndroidJUnit4::class)
class TasksViewModelTest {
// Test code
}
مرحله 5. از AndroidX Test استفاده کنید
در این مرحله، می توانید از کتابخانه AndroidX Test استفاده کنید. این شامل روش ApplicationProvider.getApplicationContex
t
است که یک متن برنامه را دریافت می کند.
- یک
TasksViewModel
با استفاده ازApplicationProvider.getApplicationContext()
از کتابخانه تست AndroidX ایجاد کنید.
TasksViewModelTest.kt
// Given a fresh ViewModel
val tasksViewModel = TasksViewModel(ApplicationProvider.getApplicationContext())
- با
addNewTask
درtasksViewModel
تماس بگیرید.
TasksViewModelTest.kt
tasksViewModel.addNewTask()
در این مرحله آزمون شما باید شبیه کد زیر باشد.
TasksViewModelTest.kt
@Test
fun addNewTask_setsNewTaskEvent() {
// Given a fresh ViewModel
val tasksViewModel = TasksViewModel(ApplicationProvider.getApplicationContext())
// When adding a new task
tasksViewModel.addNewTask()
// Then the new task event is triggered
// TODO test LiveData
}
- تست خود را اجرا کنید تا مطمئن شوید که کار می کند.
مفهوم: تست AndroidX چگونه کار می کند؟
AndroidX Test چیست؟
AndroidX Test مجموعه ای از کتابخانه ها برای آزمایش است. این شامل کلاسها و روشهایی است که نسخههایی از مؤلفههایی مانند برنامهها و فعالیتها را به شما میدهند که برای آزمایشها در نظر گرفته شدهاند. به عنوان مثال، این کدی که نوشتید نمونهای از یک تابع تست AndroidX برای دریافت زمینه برنامه است.
ApplicationProvider.getApplicationContext()
یکی از مزایای API های تست AndroidX این است که برای تست های محلی و تست های ابزاری ساخته شده اند. این خوب است زیرا:
- شما می توانید همان تست را به عنوان یک تست محلی یا یک تست ابزاری اجرا کنید.
- شما نیازی به یادگیری API های مختلف تست برای تست های محلی در مقابل تست های ابزاردار ندارید.
به عنوان مثال، چون کد خود را با استفاده از کتابخانه های تست AndroidX نوشته اید، می توانید کلاس TasksViewModelTest
خود را از پوشه test
به پوشه androidTest
منتقل کنید و تست ها همچنان اجرا می شوند. getApplicationContext()
بسته به اینکه به عنوان یک تست محلی یا ابزاری اجرا شود کمی متفاوت عمل می کند:
- اگر این یک تست ابزاری باشد، زمانی که یک شبیهساز را راهاندازی میکند یا به یک دستگاه واقعی متصل میشود، زمینه واقعی برنامه ارائه شده را دریافت میکند.
- اگر تست محلی باشد، از یک محیط اندروید شبیه سازی شده استفاده می کند.
روبولکتریک چیست؟
محیط شبیه سازی شده اندرویدی که AndroidX Test برای تست های محلی استفاده می کند توسط Robolectric ارائه شده است. Robolectric کتابخانه ای است که یک محیط اندرویدی شبیه سازی شده برای آزمایش ایجاد می کند و سریعتر از راه اندازی شبیه ساز یا اجرا بر روی یک دستگاه اجرا می شود. بدون وابستگی Robolectric، این خطا را دریافت خواهید کرد:
@RunWith(AndroidJUnit4::class)
چه کاری انجام می دهد؟
یک دونده آزمایشی یک جزء JUnit است که تست ها را اجرا می کند. بدون آزمون دونده، آزمون های شما اجرا نمی شوند. یک تست پیش فرض توسط JUnit ارائه شده است که به صورت خودکار دریافت می کنید. @RunWith
آن دونده آزمایشی پیشفرض را تعویض میکند.
اجراکننده تست AndroidJUnit4
به AndroidX Test اجازه میدهد تا بسته به اینکه آیا تستهای ابزاری یا محلی هستند، آزمایش شما را متفاوت اجرا کند.
مرحله 6. رفع هشدارهای روبولکتریک
وقتی کد را اجرا می کنید، توجه کنید که Robolectric استفاده شده است.
به دلیل AndroidX Test و اجرای تست AndroidJunit4، این کار بدون اینکه مستقیماً یک خط کد Robolectric بنویسید انجام می شود!
ممکن است متوجه دو هشدار شوید.
-
No such manifest file: ./AndroidManifest.xml
-
"WARN: Android SDK 29 requires Java 9..."
میتوانید با بهروزرسانی فایل gradle، هشدار No such manifest file: ./AndroidManifest.xml
برطرف کنید.
- خط زیر را به فایل gradle خود اضافه کنید تا از مانیفست اندروید درست استفاده شود. گزینه includeAndroidResources به شما امکان می دهد در تست های واحد خود از جمله فایل AndroidManifest خود به منابع اندروید دسترسی داشته باشید.
app/build.gradle
// Always show the result of every unit test when running via command line, even if it passes.
testOptions.unitTests {
includeAndroidResources = true
// ...
}
هشدار "WARN: Android SDK 29 requires Java 9..."
پیچیده تر است. اجرای آزمایشها در Android Q به جاوا 9 نیاز دارد . به جای تلاش برای پیکربندی Android Studio برای استفاده از جاوا 9، برای این کد لبه، هدف خود را نگه دارید و SDK را روی 28 کامپایل کنید.
به طور خلاصه:
- آزمایشهای مدل Pure View معمولاً میتوانند در مجموعه منبع
test
انجام شوند زیرا کد آنها معمولاً به Android نیاز ندارد. - میتوانید از کتابخانه تست AndroidX برای دریافت نسخههای آزمایشی مؤلفههایی مانند برنامهها و فعالیتها استفاده کنید.
- اگر نیاز به اجرای کدهای اندروید شبیهسازی شده در مجموعه منبع
test
خود دارید، میتوانید وابستگی Robolectric و حاشیهنویسی@RunWith(AndroidJUnit4::class)
را اضافه کنید.
تبریک میگوییم، شما از کتابخانه تست AndroidX و Robolectric برای اجرای آزمایش استفاده میکنید. تست شما تمام نشده است (شما هنوز یک بیانیه ادعایی ننوشته اید، فقط می گوید // TODO test LiveData
). در ادامه یاد خواهید گرفت که بیانیههای ادعایی را با LiveData
بنویسید.
در این کار، یاد خواهید گرفت که چگونه ارزش LiveData
به درستی اعلام کنید.
اینجا جایی است که بدون آزمایش مدل مشاهده addNewTask_setsNewTaskEvent
متوقف شدید.
TasksViewModelTest.kt
@Test
fun addNewTask_setsNewTaskEvent() {
// Given a fresh ViewModel
val tasksViewModel = TasksViewModel(ApplicationProvider.getApplicationContext())
// When adding a new task
tasksViewModel.addNewTask()
// Then the new task event is triggered
// TODO test LiveData
}
برای آزمایش LiveData
توصیه می شود دو کار را انجام دهید:
-
InstantTaskExecutorRule
استفاده کنید - از مشاهده
LiveData
اطمینان حاصل کنید
مرحله 1. از InstantTaskExecutorRule استفاده کنید
InstantTaskExecutorRule
یک قانون JUnit است. وقتی از آن با حاشیهنویسی @get:Rule
استفاده میکنید، باعث میشود برخی از کدها در کلاس InstantTaskExecutorRule
قبل و بعد از آزمایشها اجرا شوند (برای دیدن کد دقیق، میتوانید از میانبر صفحه کلید Command+B برای مشاهده فایل استفاده کنید).
این قانون تمام کارهای پسزمینه مربوط به اجزای معماری را در یک رشته اجرا میکند تا نتایج آزمایش به صورت همزمان و به ترتیب تکرار شونده اتفاق بیفتد. وقتی تست هایی می نویسید که شامل تست LiveData می شود، از این قانون استفاده کنید!
- وابستگی gradle را برای کتابخانه آزمایشی هسته Architecture Components (که حاوی این قانون است) اضافه کنید.
app/build.gradle
testImplementation "androidx.arch.core:core-testing:$archTestingVersion"
-
TasksViewModelTest.kt
را باز کنید -
InstantTaskExecutorRule
را داخل کلاسTasksViewModelTest
اضافه کنید.
TasksViewModelTest.kt
class TasksViewModelTest {
@get:Rule
var instantExecutorRule = InstantTaskExecutorRule()
// Other code...
}
مرحله 2. کلاس LiveDataTestUtil.kt را اضافه کنید
قدم بعدی شما این است که مطمئن شوید LiveData
که آزمایش می کنید رعایت می شود.
هنگامی که از LiveData
استفاده می کنید، معمولاً یک فعالیت یا قطعه ( LifecycleOwner
) دارید که LiveData
مشاهده می کند.
viewModel.resultLiveData.observe(fragment, Observer {
// Observer code here
})
این مشاهده مهم است. شما به ناظران فعال در LiveData
نیاز دارید
- هر رویداد
onChanged
فعال کنید. - باعث ایجاد هرگونه تغییر و تحول شود.
برای دریافت رفتار LiveData
مورد انتظار برای LiveData
مدل view خود، باید LiveData
با LifecycleOwner
مشاهده کنید.
این یک مشکل ایجاد می کند: در تست TasksViewModel
خود، فعالیت یا قطعه ای برای مشاهده LiveData
خود ندارید. برای دور زدن این موضوع، میتوانید از روش observeForever
استفاده کنید، که تضمین میکند LiveData
دائماً مشاهده میشود، بدون نیاز به LifecycleOwner
. هنگامی که observeForever
کنید، باید به یاد داشته باشید که ناظر خود را حذف کنید یا خطر نشت ناظر را به خطر بیندازید.
این چیزی شبیه به کد زیر است. بررسی کنید:
@Test
fun addNewTask_setsNewTaskEvent() {
// Given a fresh ViewModel
val tasksViewModel = TasksViewModel(ApplicationProvider.getApplicationContext())
// Create observer - no need for it to do anything!
val observer = Observer<Event<Unit>> {}
try {
// Observe the LiveData forever
tasksViewModel.newTaskEvent.observeForever(observer)
// When adding a new task
tasksViewModel.addNewTask()
// Then the new task event is triggered
val value = tasksViewModel.newTaskEvent.value
assertThat(value?.getContentIfNotHandled(), (not(nullValue())))
} finally {
// Whatever happens, don't forget to remove the observer!
tasksViewModel.newTaskEvent.removeObserver(observer)
}
}
این تعداد زیادی کد دیگ بخار برای مشاهده یک LiveData
در یک آزمایش است! چند راه برای خلاص شدن از شر این دیگ بخار وجود دارد. شما قصد دارید یک تابع افزونه به نام LiveDataTestUtil
ایجاد کنید تا اضافه کردن ناظران را سادهتر کنید.
- یک فایل Kotlin جدید به نام
LiveDataTestUtil.kt
در مجموعه منبعtest
خود بسازید.
- کد زیر را کپی و پیست کنید.
LiveDataTestUtil.kt
import androidx.annotation.VisibleForTesting
import androidx.lifecycle.LiveData
import androidx.lifecycle.Observer
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
import java.util.concurrent.TimeoutException
@VisibleForTesting(otherwise = VisibleForTesting.NONE)
fun <T> LiveData<T>.getOrAwaitValue(
time: Long = 2,
timeUnit: TimeUnit = TimeUnit.SECONDS,
afterObserve: () -> Unit = {}
): T {
var data: T? = null
val latch = CountDownLatch(1)
val observer = object : Observer<T> {
override fun onChanged(o: T?) {
data = o
latch.countDown()
this@getOrAwaitValue.removeObserver(this)
}
}
this.observeForever(observer)
try {
afterObserve.invoke()
// Don't wait indefinitely if the LiveData is not set.
if (!latch.await(time, timeUnit)) {
throw TimeoutException("LiveData value was never set.")
}
} finally {
this.removeObserver(observer)
}
@Suppress("UNCHECKED_CAST")
return data as T
}
این یک روش نسبتاً پیچیده است. این یک تابع پسوند Kotlin به نام getOrAwaitValue
ایجاد میکند که یک مشاهدهگر را اضافه میکند، مقدار LiveData
را دریافت میکند، و سپس مشاهدهگر را پاک میکند - اساساً یک نسخه کوتاه و قابل استفاده مجدد از کد observeForever
که در بالا نشان داده شده است. برای توضیح کامل این کلاس، این پست وبلاگ را بررسی کنید.
مرحله 3. از getOrAwaitValue برای نوشتن ادعا استفاده کنید
در این مرحله، شما از متد getOrAwaitValue
استفاده میکنید و یک عبارت assert مینویسید که بررسی میکند که newTaskEvent
راهاندازی شده است.
- مقدار
LiveData
را برایnewTaskEvent
با استفاده ازgetOrAwaitValue
دریافت کنید.
val value = tasksViewModel.newTaskEvent.getOrAwaitValue()
- ادعا کنید که مقدار صفر نیست.
assertThat(value.getContentIfNotHandled(), (not(nullValue())))
آزمون کامل باید مانند کد زیر باشد.
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.example.android.architecture.blueprints.todoapp.getOrAwaitValue
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.not
import org.hamcrest.Matchers.nullValue
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class TasksViewModelTest {
@get:Rule
var instantExecutorRule = InstantTaskExecutorRule()
@Test
fun addNewTask_setsNewTaskEvent() {
// Given a fresh ViewModel
val tasksViewModel = TasksViewModel(ApplicationProvider.getApplicationContext())
// When adding a new task
tasksViewModel.addNewTask()
// Then the new task event is triggered
val value = tasksViewModel.newTaskEvent.getOrAwaitValue()
assertThat(value.getContentIfNotHandled(), not(nullValue()))
}
}
- کد خود را اجرا کنید و گذرگاه آزمون را تماشا کنید!
اکنون که دیده اید که چگونه یک آزمون بنویسید ، یکی را به تنهایی بنویسید. در این مرحله ، با استفاده از مهارتهایی که آموخته اید ، نوشتن یک تست TasksViewModel
را تمرین کنید.
مرحله 1 آزمون ViewModel خود را بنویسید
شما setFilterAllTasks_tasksAddViewVisible()
را می نویسید. این تست باید بررسی کند که اگر نوع فیلتر خود را برای نشان دادن تمام کارها تنظیم کرده اید ، که دکمه Add Task قابل مشاهده است.
- با استفاده از
addNewTask_setsNewTaskEvent()
برای مرجع ، یک تست را درTasksViewModelTest
به نامsetFilterAllTasks_tasksAddViewVisible()
بنویسید که حالت فیلتر را بر رویALL_TASKS
تنظیم می کند و ادعا می کند کهtasksAddViewVisible
livivedatatrue
است.
برای شروع از کد زیر استفاده کنید.
TaskSviewModeltest
@Test
fun setFilterAllTasks_tasksAddViewVisible() {
// Given a fresh ViewModel
// When the filter type is ALL_TASKS
// Then the "Add task" action is visible
}
توجه:
- Enum
TasksFilterType
برای همه کارهاALL_TASKS.
- قابلیت مشاهده دکمه برای افزودن یک کار توسط TaskSaddviewVisible
LiveData
کنترل می شودtasksAddViewVisible.
- آزمون خود را اجرا کنید.
مرحله 2 آزمون خود را با راه حل مقایسه کنید
راه حل خود را با راه حل زیر مقایسه کنید.
TaskSviewModeltest
@Test
fun setFilterAllTasks_tasksAddViewVisible() {
// Given a fresh ViewModel
val tasksViewModel = TasksViewModel(ApplicationProvider.getApplicationContext())
// When the filter type is ALL_TASKS
tasksViewModel.setFiltering(TasksFilterType.ALL_TASKS)
// Then the "Add task" action is visible
assertThat(tasksViewModel.tasksAddViewVisible.getOrAwaitValue(), `is`(true))
}
بررسی کنید که آیا موارد زیر را انجام می دهید:
- شما
tasksViewModel
خود را با استفاده از همان بیانیه AndroidxApplicationProvider.getApplicationContext()
ایجاد می کنید. - شما با روش
setFiltering
تماس می گیرید و در enum نوع فیلترALL_TASKS
عبور می کنید. - شما با استفاده از روش
getOrAwaitNextValue
،tasksAddViewVisible
درست است.
مرحله 3. یک قانون را اضافه کنید
توجه کنید که چگونه در شروع هر دو تست خود ، یک TasksViewModel
تعریف می کنید.
TaskSviewModeltest
// Given a fresh ViewModel
val tasksViewModel = TasksViewModel(ApplicationProvider.getApplicationContext())
هنگامی که کد تنظیمات را برای چندین آزمایش تکرار کرده اید ، می توانید از حاشیه نویسی برای ایجاد یک روش تنظیم و حذف کد مکرر استفاده کنید. از آنجا که همه این تست ها قصد دارند TasksViewModel
را آزمایش کنند و به یک مدل نمایش نیاز دارند ، این کد را به یک بلوک @Before
منتقل کنید.
- ایجاد یک متغیر نمونه
lateinit
به نامtasksViewModel|
. - روشی به نام
setupViewModel
ایجاد کنید. - آن را با
@Before
حاشیه نویسی کنید. - کد فوری مدل View را به
setupViewModel
منتقل کنید.
TaskSviewModeltest
// Subject under test
private lateinit var tasksViewModel: TasksViewModel
@Before
fun setupViewModel() {
tasksViewModel = TasksViewModel(ApplicationProvider.getApplicationContext())
}
- کد خود را اجرا کنید!
هشدار
موارد زیر را انجام ندهید ، شروع نکنید
tasksViewModel
با تعریف آن:
val tasksViewModel = TasksViewModel(ApplicationProvider.getApplicationContext())
این باعث می شود همان نمونه برای همه آزمایشات استفاده شود. این چیزی است که شما باید از آن جلوگیری کنید زیرا هر آزمایش باید نمونه جدیدی از موضوع تحت آزمایش داشته باشد (ViewModel در این مورد).
کد نهایی شما برای TasksViewModelTest
باید مانند کد زیر باشد.
TaskSviewModeltest
@RunWith(AndroidJUnit4::class)
class TasksViewModelTest {
// Subject under test
private lateinit var tasksViewModel: TasksViewModel
// Executes each task synchronously using Architecture Components.
@get:Rule
var instantExecutorRule = InstantTaskExecutorRule()
@Before
fun setupViewModel() {
tasksViewModel = TasksViewModel(ApplicationProvider.getApplicationContext())
}
@Test
fun addNewTask_setsNewTaskEvent() {
// When adding a new task
tasksViewModel.addNewTask()
// Then the new task event is triggered
val value = tasksViewModel.newTaskEvent.awaitNextValue()
assertThat(
value?.getContentIfNotHandled(), (not(nullValue()))
)
}
@Test
fun getTasksAddViewVisible() {
// When the filter type is ALL_TASKS
tasksViewModel.setFiltering(TasksFilterType.ALL_TASKS)
// Then the "Add task" action is visible
assertThat(tasksViewModel.tasksAddViewVisible.awaitNextValue(), `is`(true))
}
}
برای دیدن تفاوت بین کدی که شروع کرده اید و کد نهایی ، اینجا را کلیک کنید.
برای بارگیری کد برای CodeLab تمام شده ، می توانید از دستور GIT در زیر استفاده کنید:
$ git clone https://github.com/googlecodelabs/android-testing.git $ cd android-testing $ git checkout end_codelab_1
از طرف دیگر می توانید مخزن را به عنوان یک فایل ZIP بارگیری کنید ، آن را از حالت فشرده خارج کنید و آن را در Android Studio باز کنید.
این CodeLab را پوشش داده است:
- نحوه اجرای تست ها از Android Studio.
- تفاوت بین تست های محلی (
test
) و ابزار دقیق (androidTest
). - نحوه نوشتن تست های واحد محلی با استفاده از Junit و Hamcrest .
- تنظیم تست های ViewModel با کتابخانه تست Androidx .
دوره Udacity:
مستندات توسعه دهنده اندروید:
- راهنمای معماری اپلیکیشن
- JUnit4
- قید
- کتابخانه آزمایش رولکتریک
- کتابخانه تست Androidx
- کتابخانه تست اصلی Androidx Components Components
- مجموعه منبع
- از خط فرمان تست کنید
ویدئوها:
دیگر:
برای پیوندها به سایر CodeLabs در این دوره ، به صفحه Advanced Android در Kotlin Codelabs Landing مراجعه کنید.