Lớp học lập trình này nằm trong khóa học Nâng cao về Android trong Kotlin. Bạn sẽ nhận được nhiều giá trị nhất từ khóa học này nếu bạn làm việc qua các lớp học lập trình theo trình tự, nhưng bạn không bắt buộc phải làm vậy. Tất cả các lớp học lập trình đều có trên trang đích của các lớp học lập trình Android nâng cao trong Kotlin.
Giới thiệu
Khi triển khai tính năng đầu tiên của ứng dụng đầu tiên, bạn có thể chạy mã để xác minh rằng ứng dụng hoạt động như mong đợi. Bạn đã kiểm tra, mặc dù thử nghiệm thủ công. Khi tiếp tục thêm và cập nhật các tính năng, bạn có thể vẫn tiếp tục chạy mã và xác minh mã hoạt động. Tuy nhiên, làm theo cách thủ công mỗi lần sẽ nhàm chán, dễ xảy ra sai sót và không mở rộng quy mô.
Máy tính là một công cụ hiệu quả để mở rộng quy mô và tự động hóa! Do đó, các nhà phát triển tại các công ty lớn và nhỏ viết thử nghiệm tự động, là các thử nghiệm do phần mềm chạy và không yêu cầu bạn phải vận hành ứng dụng theo cách thủ công để xác minh mã hoạt động.
Nội dung bạn sẽ tìm hiểu trong chuỗi lớp học lập trình này là cách tạo tập hợp các thử nghiệm (được gọi là bộ thử nghiệm) cho ứng dụng thực tế.
Lớp học lập trình đầu tiên này cung cấp thông tin cơ bản về cách thử nghiệm trên Android. Bạn có thể viết các thử nghiệm đầu tiên và tìm hiểu cách thử nghiệm LiveData
và ViewModel
.
Kiến thức bạn cần có
Bạn cần thông thạo:
- Ngôn ngữ lập trình Kotlin
- Các thư viện cốt lõi sau đây của Android Jetpack:
ViewModel
vàLiveData
- Cấu trúc ứng dụng, theo mẫu từ Hướng dẫn về cấu trúc ứng dụng và lớp học lập trình Android Fundamentals
Kiến thức bạn sẽ học được
Bạn sẽ tìm hiểu về các chủ đề sau:
- Cách viết và chạy thử nghiệm đơn vị trên Android
- Cách sử dụng tính năng Phát triển theo hướng thử nghiệm
- Cách chọn thử nghiệm đo lường và thử nghiệm tại địa phương
Bạn sẽ tìm hiểu về các thư viện và khái niệm mã sau:
- JUnit 4
- Hamcrest
- Thư viện thử nghiệm AndroidX
- Thư viện thử nghiệm cốt lõi của các thành phần cấu trúc AndroidX
Bạn sẽ thực hiện
- Thiết lập, chạy và diễn giải cả thử nghiệm địa phương và thử nghiệm đo lường trong Android.
- Viết các bài kiểm tra đơn vị trong Android bằng JUnit4 và Hamcrest.
- Viết các thử nghiệm
LiveData
vàViewModel
đơn giản.
Trong chuỗi lớp học lập trình này, bạn sẽ làm việc với ứng dụng TO-DO Notes. Ứng dụng này cho phép bạn viết ra các công việc để hoàn thành và hiển thị chúng trong danh sách. Sau đó, bạn có thể đánh dấu lời nhắc là đã hoàn thành, hay không, lọc hoặc xóa lời nhắc.
Ứng dụng này được viết bằng Kotlin, có nhiều màn hình, sử dụng các thành phần Jetpack và tuân theo cấu trúc từ Hướng dẫn về cấu trúc ứng dụng. Bằng cách tìm hiểu cách thử nghiệm ứng dụng này, bạn có thể thử nghiệm các ứng dụng sử dụng cùng thư viện và cấu trúc.
Để bắt đầu, hãy tải mã xuống:
Ngoài ra, bạn có thể sao chép kho lưu trữ GitHub cho mã:
$ git clone https://github.com/googlecodelabs/android-testing.git $ cd android-testing $ git checkout starter_code
Trong nhiệm vụ này, bạn sẽ chạy ứng dụng và khám phá cơ sở mã.
Bước 1: Chạy ứng dụng mẫu
Sau khi bạn tải ứng dụng TO-DO xuống, hãy mở ứng dụng này trong Android Studio và chạy ứng dụng. Nên biên dịch. Khám phá ứng dụng bằng cách làm như sau:
- Tạo một việc mới cần làm bằng nút dấu cộng thao tác nổi. Nhập tiêu đề trước, sau đó nhập thêm thông tin về việc cần làm. Lưu mật khẩu bằng kiểm tra FAB màu xanh lục.
- Trong danh sách việc cần làm, hãy nhấp vào tiêu đề của việc cần làm bạn vừa hoàn thành, rồi xem màn hình chi tiết về việc cần làm đó để xem phần mô tả còn lại.
- Trong danh sách hoặc trên màn hình chi tiết, hãy đánh dấu vào hộp kiểm của việc cần làm đó để đặt trạng thái cho Việc cần làm.
- Quay lại màn hình việc cần làm, mở trình đơn bộ lọc rồi lọc việc cần làm theo trạng thái Đang hoạt động và Đã hoàn thành.
- Mở ngăn điều hướng và nhấp vào Thống kê.
- Quay lại màn hình tổng quan và từ trình đơn ngăn điều hướng, hãy chọn Xóa đã hoàn tất để xóa tất cả việc cần làm có trạng thái Đã hoàn thành
Bước 2: Khám phá mã ứng dụng mẫu
Ứng dụng TO-DO được xây dựng dựa trên mẫu kiến trúc và thử nghiệm Bản vẽ kiến trúc phổ biến (sử dụng phiên bản cấu trúc phản hồi của mẫu). Ứng dụng này tuân theo cấu trúc từ Hướng dẫn về cấu trúc ứng dụng. Chế độ xem này sử dụng ViewModel với các Mảnh, kho lưu trữ và Phòng. Nếu bạn quen thuộc với bất kỳ ví dụ nào dưới đây, ứng dụng này có cấu trúc tương tự:
- Phòng có Lớp học mã
- Các lớp học đào tạo về lập trình Android Kotlin Fundamentals
- Lớp học lập trình nâng cao về Android
- Mẫu Android Sunflower
- Phát triển ứng dụng Android bằng khóa đào tạo Kotlin Udacity
Điều quan trọng là bạn phải hiểu được cấu trúc chung của ứng dụng hơn là hiểu sâu về logic ở bất kỳ lớp nào.
Dưới đây là phần tóm tắt các gói bạn sẽ tìm thấy:
Gói hàng: | |
| Thêm hoặc chỉnh sửa màn hình việc cần làm: Mã lớp giao diện người dùng để thêm hoặc chỉnh sửa việc cần làm. |
| Lớp dữ liệu: Lớp này xử lý lớp dữ liệu của các tác vụ. Thẻ này chứa cơ sở dữ liệu, mạng và mã kho lưu trữ. |
| Màn hình thống kê: Mã lớp giao diện người dùng cho màn hình thống kê. |
| Màn hình thông tin chi tiết về việc cần làm: Mã lớp giao diện người dùng cho một việc cần làm. |
| Màn hình việc cần làm: Mã lớp giao diện người dùng cho danh sách tất cả việc cần làm. |
| Lớp tiện ích: Lớp học dùng chung được dùng trong nhiều phần của ứng dụng, ví dụ: cho bố cục làm mới khi vuốt được sử dụng trên nhiều màn hình. |
Lớp dữ liệu (.data)
Ứng dụng này bao gồm một lớp mạng mô phỏng, trong gói remote và một lớp cơ sở dữ liệu, trong gói local. Để đơn giản hóa, trong dự án này, lớp mạng được mô phỏng chỉ bằng HashMap
có độ trễ, thay vì thực hiện các yêu cầu mạng thực.
DefaultTasksRepository
tọa độ hoặc dàn xếp giữa lớp mạng và lớp cơ sở dữ liệu, đồng thời là dữ liệu trả về dữ liệu cho lớp giao diện người dùng.
Lớp giao diện người dùng ( .addedittask, .stats, .taskdetail, .tasks)
Mỗi gói lớp giao diện người dùng chứa một mảnh và mô hình chế độ xem, cùng với bất kỳ lớp nào khác cần thiết cho giao diện người dùng (chẳng hạn như bộ chuyển đổi cho danh sách tác vụ). TaskActivity
là hoạt động chứa tất cả các mảnh.
Điều hướng
Hoạt động điều hướng cho ứng dụng do Thành phần điều hướng kiểm soát. Tên này được xác định trong tệp nav_graph.xml
. Trình kích hoạt được kích hoạt trong các mô hình chế độ xem bằng cách sử dụng lớp Event
; các mô hình chế độ xem cũng xác định các đối số cần chuyển. Các mảnh quan sát Event
và thực hiện việc di chuyển thực tế giữa các màn hình.
Trong nhiệm vụ này, bạn sẽ chạy các thử nghiệm đầu tiên.
- Trong Android Studio, hãy mở ngăn Dự án rồi tìm ba thư mục sau:
com.example.android.architecture.blueprints.todoapp
com.example.android.architecture.blueprints.todoapp (androidTest)
com.example.android.architecture.blueprints.todoapp (test)
Những thư mục này được gọi là nhóm nguồn. Tập hợp nguồn là các thư mục chứa mã nguồn cho ứng dụng của bạn. Các nhóm nguồn có màu xanh lục (androidTest và test) chứa các thử nghiệm của bạn. Theo mặc định, khi tạo một dự án Android mới, bạn sẽ nhận được 3 nhóm nguồn sau đây. Các yếu tố này là:
main
: Chứa mã ứng dụng. Mã này được chia sẻ giữa tất cả các phiên bản khác nhau của ứng dụng mà bạn có thể xây dựng (được gọi là biến thể bản dựng)androidTest
: Chứa các thử nghiệm được gọi là thử nghiệm đo lường.test
: Chứa các thử nghiệm được gọi là thử nghiệm tại địa phương.
Sự khác biệt giữa thử nghiệm cục bộ và thử nghiệm theo công cụ là cách chạy thử nghiệm.
Thử nghiệm cục bộ (test
nhóm nguồn)
Những thử nghiệm này được chạy cục bộ trên JVM của máy phát triển và không yêu cầu trình mô phỏng hoặc thiết bị thực. Do đó, chúng chạy nhanh nhưng độ chân thực sẽ thấp hơn, có nghĩa là chúng hoạt động ít hơn so với trong thế giới thực.
Trong thử nghiệm cục bộ trên Android Studio, biểu tượng hình tam giác màu xanh lục và màu đỏ được biểu thị.
Thử nghiệm đo lường (androidTest
nhóm nguồn)
Những thử nghiệm này chạy trên thiết bị Android thực hoặc được mô phỏng, do đó chúng phản ánh điều gì sẽ xảy ra trong thế giới thực, nhưng cũng chậm hơn nhiều.
Trong các thử nghiệm đo lường trên Android Studio, biểu tượng Android được biểu thị bằng biểu tượng hình tam giác màu xanh lục và màu đỏ.
Bước 1: Chạy thử nghiệm cục bộ
- Mở thư mục
test
cho đến khi bạn thấy tệp ExampleUnitTest.kt. - Nhấp chuột phải vào tệp và chọn Chạy ExampleUnitTest.
Bạn sẽ thấy kết quả sau trong cửa sổ Chạy ở cuối màn hình:
- Lưu ý các dấu kiểm màu xanh lục và mở rộng kết quả thử nghiệm để xác nhận rằng một thử nghiệm có tên là
addition_isCorrect
đã vượt qua. Thật tuyệt khi biết rằng tính năng bổ sung hoạt động như mong đợi!
Bước 2: Không chạy thử nghiệm
Dưới đây là thử nghiệm mà bạn vừa chạy.
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)
}
}
Lưu ý rằng các thử nghiệm
- là một lớp ở một trong các nhóm nguồn thử nghiệm.
- chứa các hàm bắt đầu bằng chú thích
@Test
(mỗi hàm là một thử nghiệm duy nhất). - có chứa các tuyên bố xác nhận.
Android dùng thư viện thử nghiệm JUnit để thử nghiệm (trong lớp học lập trình JUnit4 này). Cả hai loại này, từ xác nhận và chú thích @Test
đều đến từ JUnit.
Cài đặt là cốt lõi trong thử nghiệm của bạn. Đó là câu lệnh mã kiểm tra xem mã hoặc ứng dụng của bạn có hoạt động như mong đợi hay không. Trong trường hợp này, khẳng định là assertEquals(4, 2 + 2)
kiểm tra để đảm bảo rằng 4 bằng 2 + 2.
Để xem thử nghiệm không thành công trông như thế nào, hãy thêm xác nhận rằng bạn có thể dễ dàng thấy không thành công. Nó sẽ kiểm tra rằng 3 bằng 1 + 1.
- Thêm
assertEquals(3, 1 + 1)
vào thử nghiệmaddition_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
}
}
- Chạy thử nghiệm.
- Trong kết quả thử nghiệm, hãy thấy dấu X bên cạnh thử nghiệm.
- Ngoài ra, hãy lưu ý:
- Một lần xác nhận không thành công sẽ không thể kiểm tra toàn bộ thử nghiệm.
- Bạn nhận được giá trị dự kiến (3) so với giá trị thực tế được tính toán (2).
- Bạn sẽ được chuyển đến dòng của khẳng định không thành công
(ExampleUnitTest.kt:16)
.
Bước 3: Chạy thử nghiệm đo lường
Các thử nghiệm đo lường nằm trong nhóm nguồn androidTest
.
- Mở tập hợp nguồn
androidTest
. - Chạy thử nghiệm tên là
ExampleInstrumentedTest
.
ExampleTooledTest
@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)
}
}
Không giống như thử nghiệm cục bộ, thử nghiệm này chạy trên một thiết bị (trong ví dụ dưới đây là điện thoại Pixel 2 được mô phỏng):
Nếu bạn đã đính kèm thiết bị hoặc trình mô phỏng đang chạy, thì bạn sẽ thấy thử nghiệm chạy trên trình mô phỏng.
Trong nhiệm vụ này, bạn sẽ viết các bài kiểm tra cho getActiveAndCompleteStats
, tính toán tỷ lệ phần trăm số liệu thống kê nhiệm vụ đang hoạt động và hoàn chỉnh cho ứng dụng của bạn. Bạn có thể xem các số này trên màn hình thống kê của ứng dụng.
Bước 1: Tạo lớp kiểm tra
- Trong nhóm nguồn
main
, trongtodoapp.statistics
, hãy mởStatisticsUtils.kt
. - Tìm hàm
getActiveAndCompletedStats
.
ThốngUtils.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)
Hàm getActiveAndCompletedStats
chấp nhận danh sách các tác vụ và trả về một StatsResult
. StatsResult
là một lớp dữ liệu chứa 2 số, tỷ lệ phần trăm việc cần làm đã hoàn thành và tỷ lệ phần trăm mà đang hoạt động.
Android Studio cung cấp cho bạn các công cụ để tạo mã thử nghiệm giúp bạn triển khai các thử nghiệm cho chức năng này.
- Nhấp chuột phải vào
getActiveAndCompletedStats
và chọn Tạo > Thử nghiệm.
Hộp thoại Tạo thử nghiệm sẽ mở ra:
- Thay đổi Tên lớp: thành
StatisticsUtilsTest
(thay vìStatisticsUtilsKtTest
; sẽ không nhỉnh hơn khi không có KT trong tên lớp thử nghiệm). - Giữ các tùy chọn mặc định còn lại. JUnit 4 là thư viện thử nghiệm phù hợp. Gói đích là chính xác (bắt chước vị trí của lớp
StatisticsUtils
) và bạn không cần phải chọn bất kỳ hộp kiểm nào (điều này chỉ tạo thêm mã, nhưng bạn sẽ viết thử nghiệm của mình từ đầu). - Nhấn OK
Hộp thoại Chọn thư mục đích sẽ mở ra:
Bạn sẽ thực hiện một bài kiểm tra cục bộ vì hàm của bạn đang tính toán và sẽ không bao gồm bất kỳ mã cụ thể nào trên Android. Vì vậy, bạn không cần chạy quảng cáo trên thiết bị thực hoặc được mô phỏng.
- Chọn thư mục
test
(không phảiandroidTest
) vì bạn sẽ viết bài kiểm tra tại địa phương. - Nhấp vào OK.
- Xin lưu ý rằng lớp
StatisticsUtilsTest
đã tạo trongtest/statistics/
.
Bước 2: Viết hàm kiểm tra đầu tiên của bạn
Bạn sẽ viết một thử nghiệm kiểm tra:
- nếu không có tác vụ đã hoàn thành nào và một tác vụ đang hoạt động,
- rằng tỷ lệ phần trăm của số thử nghiệm đang hoạt động là 100%,
- và tỷ lệ phần trăm nhiệm vụ đã hoàn thành là 0%.
- Mở
StatisticsUtilsTest
. - Tạo một hàm có tên là
getActiveAndCompletedStats_noCompleted_returnsHundredZero
.
ThốngUtilsTest.kt
class StatisticsUtilsTest {
fun getActiveAndCompletedStats_noCompleted_returnsHundredZero() {
// Create an active task
// Call your function
// Check the result
}
}
- Thêm chú thích
@Test
phía trên tên hàm để cho biết đó là thử nghiệm. - Tạo một danh sách việc cần làm.
// Create an active task
val tasks = listOf<Task>(
Task("title", "desc", isCompleted = false)
)
- Gọi
getActiveAndCompletedStats
cho những việc cần làm này.
// Call your function
val result = getActiveAndCompletedStats(tasks)
- Kiểm tra để chắc chắn
result
là những gì bạn mong đợi, bằng cách sử dụng các lời khẳng định.
// Check the result
assertEquals(result.completedTasksPercent, 0f)
assertEquals(result.activeTasksPercent, 100f)
Sau đây là mã hoàn chỉnh.
ThốngUtilsTest.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)
}
}
- Chạy thử nghiệm (Nhấp chuột phải vào
StatisticsUtilsTest
rồi chọn Chạy).
Thẻ phải vượt qua:
Bước 3: Thêm phần phụ thuộc Hamcrest
Vì thử nghiệm của bạn hoạt động như một tài liệu về mã hoạt động nên sẽ rất hữu ích khi người dùng có thể đọc được. So sánh hai lời khẳng định sau đây:
assertEquals(result.completedTasksPercent, 0f)
// versus
assertThat(result.completedTasksPercent, `is`(0f))
Câu xác nhận thứ hai giống với câu nói của người khác. Thông tin này được viết bằng khung xác nhận có tên Hamcrest. Một công cụ tốt khác để viết tuyên bố có thể đọc được là Thư viện trung thực. Bạn sẽ sử dụng Hamcrest trong lớp học lập trình này để viết phần khẳng định.
- Mở
build.grade (Module: app)
và thêm phần phụ thuộc sau.
app/build.gradle
dependencies {
// Other dependencies
testImplementation "org.hamcrest:hamcrest-all:$hamcrestVersion"
}
Thông thường, bạn sử dụng implementation
khi thêm phần phụ thuộc, nhưng bạn đang sử dụng testImplementation
ở đây. Khi bạn sẵn sàng chia sẻ ứng dụng của mình với mọi người, tốt nhất bạn không nên mở rộng kích thước APK với bất kỳ mã hoặc phần phụ thuộc thử nghiệm nào trong ứng dụng của mình. Bạn có thể chỉ định xem một thư viện có được đưa vào mã thử nghiệm hoặc chính không bằng cách sử dụng cấu hình gradle. Các cấu hình phổ biến nhất là:
implementation
—Phần phụ thuộc này có sẵn trong tất cả các nhóm nguồn, bao gồm cả các nhóm nguồn thử nghiệm.testImplementation
—Phần phụ thuộc chỉ có trong nhóm nguồn thử nghiệm.androidTestImplementation
—Phần phụ thuộc này chỉ có trong nhóm nguồnandroidTest
.
Cấu hình mà bạn sử dụng sẽ xác định vị trí có thể sử dụng phần phụ thuộc này. Nếu bạn viết:
testImplementation "org.hamcrest:hamcrest-all:$hamcrestVersion"
Điều này có nghĩa là Hamcrest sẽ chỉ có trong nhóm thử nghiệm. Điều này cũng đảm bảo rằng Hamcrest sẽ không được đưa vào ứng dụng cuối cùng của bạn.
Bước 4: Sử dụng Hamcrest để viết lời khẳng định
- Hãy cập nhật thử nghiệm
getActiveAndCompletedStats_noCompleted_returnsHundredZero()
để sử dụngassertThat
thay vìassertEquals
của Hamcrest.
// REPLACE
assertEquals(result.completedTasksPercent, 0f)
assertEquals(result.activeTasksPercent, 100f)
// WITH
assertThat(result.activeTasksPercent, `is`(100f))
assertThat(result.completedTasksPercent, `is`(0f))
Lưu ý rằng bạn có thể sử dụng tính năng nhập import org.hamcrest.Matchers.`is`
nếu được nhắc.
Thử nghiệm cuối cùng sẽ trông giống mã bên dưới.
ThốngUtilsTest.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))
}
}
- Hãy chạy thử nghiệm đã cập nhật để chắc chắn rằng tính năng này vẫn hoạt động!
Lớp học lập trình này sẽ không hướng dẫn bạn về tất cả các chi tiết trong Hamcrest, vì vậy nếu bạn muốn tìm hiểu thêm, hãy xem hướng dẫn chính thức.
Đây là nhiệm vụ không bắt buộc để thực hành.
Trong nhiệm vụ này, bạn sẽ viết nhiều thử nghiệm hơn bằng cách sử dụng JUnit và Hamcrest. Bạn cũng sẽ viết các thử nghiệm bằng cách sử dụng chiến lược từ phương pháp thực hành Chương trình phát triển theo hướng thử nghiệm. Phát triển theo hướng thử nghiệm hay TDD là một trường lập trình cho rằng thay vì viết mã tính năng trước, bạn hãy viết các thử nghiệm của mình trước. Sau đó, bạn viết mã tính năng của mình với mục tiêu vượt qua thử nghiệm.
Bước 1. Viết mã kiểm thử
Viết bài kiểm tra khi bạn có một danh sách việc cần làm thông thường:
- Nếu có một công việc đã hoàn thành và không có công việc nào đang hoạt động, thì tỷ lệ phần trăm
activeTasks
sẽ là0f
và tỷ lệ phần trăm việc cần làm đã hoàn thành sẽ là100f
. - Nếu có hai việc cần làm đã hoàn thành và ba việc cần làm đang hoạt động, thì tỷ lệ phần trăm hoàn thành phải là
40f
và tỷ lệ phần trăm đang hoạt động phải là60f
.
Bước 2. Viết bài kiểm tra để tìm lỗi
Mã cho getActiveAndCompletedStats
như đã viết có lỗi. Lưu ý rằng trình xử lý không xử lý đúng cách những gì sẽ xảy ra nếu danh sách trống hoặc rỗng. Trong cả hai trường hợp này, cả hai phần trăm đều phải bằng 0.
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()
)
}
Để sửa mã và viết thử nghiệm, bạn sẽ sử dụng tính năng phát triển theo hướng thử nghiệm. Phát triển theo hướng thử nghiệm theo những bước sau.
- Viết bài kiểm tra bằng cách sử dụng cấu trúc Cho, Khi, Sau đó và với tên theo quy ước.
- Xác nhận kiểm tra không thành công.
- Viết mã tối thiểu để vượt qua bài kiểm tra.
- Lặp lại cho tất cả thử nghiệm!
Thay vì bắt đầu bằng cách sửa lỗi, bạn sẽ bắt đầu bằng cách viết các thử nghiệm trước. Sau đó, bạn có thể xác nhận rằng bạn có các thử nghiệm bảo vệ bạn khỏi việc vô tình giới thiệu lại các lỗi này trong tương lai.
- Nếu có danh sách trống (
emptyList()
), thì cả hai tỷ lệ phần trăm đều phải là 0f. - Nếu xảy ra lỗi khi tải việc cần làm, thì danh sách sẽ là
null
và cả hai tỷ lệ phần trăm đều phải là 0f. - Chạy thử nghiệm của bạn và xác nhận rằng chúng không đạt:
Bước 3. Sửa lỗi
Giờ đây, khi bạn đã kiểm tra xong, hãy khắc phục lỗi.
- Sửa lỗi trong
getActiveAndCompletedStats
bằng cách trả về0f
nếutasks
hoặcnull
trống:
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
)
}
}
- Chạy thử nghiệm lại và xác nhận rằng tất cả thử nghiệm hiện đã vượt qua!
Bằng cách làm theo TDD và viết bài kiểm tra trước, bạn đã giúp đảm bảo rằng:
- Chức năng mới luôn có các thử nghiệm liên quan; do đó, thử nghiệm của bạn hoạt động như một tài liệu về mã của bạn.
- Các thử nghiệm của bạn sẽ kiểm tra kết quả chính xác và bảo vệ khỏi những lỗi mà bạn đã thấy.
Giải pháp: Viết thêm các thử nghiệm
Dưới đây là tất cả thử nghiệm và mã tính năng tương ứng.
ThốngUtilsTest.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))
}
}
ThốngUtils.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
)
}
}
Bạn đã làm tốt công việc cơ bản của quá trình viết và chạy thử nghiệm! Tiếp theo, bạn sẽ tìm hiểu cách viết các bài kiểm tra ViewModel
và LiveData
cơ bản.
Trong phần còn lại của lớp học lập trình, bạn sẽ tìm hiểu cách viết bài kiểm tra cho hai lớp học Android phổ biến trên hầu hết các ứng dụng – ViewModel
và LiveData
.
Bạn bắt đầu bằng cách viết bài kiểm tra cho TasksViewModel
.
Bạn sẽ tập trung vào các thử nghiệm có toàn bộ logic trong mô hình chế độ xem và không dựa vào mã kho lưu trữ. Mã kho lưu trữ bao gồm mã không đồng bộ, cơ sở dữ liệu và lệnh gọi mạng, tất cả đều làm tăng độ phức tạp của thử nghiệm. Bạn sẽ tránh điều đó ngay bây giờ và tập trung vào việc viết các thử nghiệm cho chức năng ViewModel không trực tiếp kiểm tra bất kỳ điều gì trong kho lưu trữ.
Thử nghiệm mà bạn sẽ viết sẽ kiểm tra để đảm bảo rằng khi bạn gọi phương thức addNewTask
, Event
để mở cửa sổ việc cần làm mới sẽ được kích hoạt. Đây là mã ứng dụng mà bạn sẽ thử nghiệm.
TasksViewModel.kt
fun addNewTask() {
_newTaskEvent.value = Event(Unit)
}
Bước 1. Tạo một lớp TasksViewModelTest
Làm theo các bước tương tự như đối với StatisticsUtilTest
, ở bước này, bạn tạo một tệp thử nghiệm cho TasksViewModelTest
.
- Mở lớp học mà bạn muốn kiểm tra, trong gói
tasks
,TasksViewModel.
- Trong mã, hãy nhấp chuột phải vào tên lớp
TasksViewModel
-> Generate -> Test.
- Trên màn hình Tạo thử nghiệm, hãy nhấp vào OK để chấp nhận (không cần thay đổi bất kỳ chế độ cài đặt mặc định nào).
- Trên hộp thoại Chọn thư mục đích, hãy chọn thư mục thử nghiệm.
Bước 2. Bắt đầu viết Bài kiểm tra ViewModel
Trong bước này, bạn thêm một thử nghiệm mô hình chế độ xem để kiểm tra rằng khi bạn gọi phương thức addNewTask
, Event
để mở cửa sổ tác vụ mới sẽ được kích hoạt.
- Tạo một thử nghiệm mới có tên là
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
}
}
Vậy còn bối cảnh đăng ký thì sao?
Khi bạn tạo một bản sao của TasksViewModel
để kiểm tra, hàm dựng của nó sẽ cần có Ngữ cảnh ứng dụng. Nhưng trong thử nghiệm này, bạn không tạo một ứng dụng đầy đủ với các hoạt động và giao diện người dùng cũng như các mảnh, vậy làm thế nào để bạn có được ngữ cảnh ứng dụng?
TasksViewModelTest.kt
// Given a fresh ViewModel
val tasksViewModel = TasksViewModel(???)
Thư viện Thử nghiệm AndroidX bao gồm các lớp và phương thức cung cấp cho bạn phiên bản thành phần như Ứng dụng và Hoạt động dành cho mục đích thử nghiệm. Khi bạn có thử nghiệm cục bộ mà trong đó bạn cần có các lớp khung khung Android được mô phỏng(chẳng hạn như Ngữ cảnh ứng dụng), hãy làm theo các bước sau để thiết lập đúng thử nghiệm AndroidX:
- Thêm các phần phụ thuộc bên ngoài và lõi Thử nghiệm AndroidX
- Thêm phần phụ thuộc của Thư viện thử nghiệm Robolectric
- Chú thích lớp bằng trình chạy kiểm tra AndroidJunit4
- Viết mã thử nghiệm AndroidX
Bạn sẽ hoàn thành các bước này và sau đó hiểu rõ chức năng của chúng.
Bước 3. Thêm các phần phụ thuộc gradle
- Hãy sao chép các phần phụ thuộc này vào tệp
build.gradle
của mô-đun ứng dụng để thêm phần phụ thuộc Thử nghiệm AndroidX và cốt lõi bên ngoài, cũng như phần phụ thuộc thử nghiệm 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"
Bước 4. Thêm trình chạy kiểm tra JUnit
- Thêm
@RunWith(AndroidJUnit4::class)
phía trên lớp kiểm tra.
TasksViewModelTest.kt
@RunWith(AndroidJUnit4::class)
class TasksViewModelTest {
// Test code
}
Bước 5. Sử dụng thử nghiệm AndroidX
Tại thời điểm này, bạn có thể sử dụng thư viện Thử nghiệm AndroidX. Điều này bao gồm phương thức ApplicationProvider.getApplicationContex
t
, lấy bối cảnh ứng dụng.
- Tạo
TasksViewModel
bằngApplicationProvider.getApplicationContext()
từ thư viện thử nghiệm AndroidX.
TasksViewModelTest.kt
// Given a fresh ViewModel
val tasksViewModel = TasksViewModel(ApplicationProvider.getApplicationContext())
- Gọi cho
addNewTask
trêntasksViewModel
.
TasksViewModelTest.kt
tasksViewModel.addNewTask()
Khi đó, thử nghiệm của bạn sẽ trông giống như mã bên dưới.
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
}
- Chạy thử nghiệm của bạn để xác nhận tính năng này hoạt động.
Khái niệm: Tính năng Kiểm tra AndroidX hoạt động như thế nào?
Thử nghiệm AndroidX là gì?
Thử nghiệm AndroidX là một tập hợp các thư viện để thử nghiệm. Nền tảng này bao gồm các lớp và phương thức cung cấp cho bạn phiên bản thành phần như Ứng dụng và Hoạt động dành cho mục đích thử nghiệm. Ví dụ: mã bạn đã viết này là ví dụ về hàm Kiểm tra AndroidX để nhận ngữ cảnh ứng dụng.
ApplicationProvider.getApplicationContext()
Một trong những lợi ích của API Thử nghiệm AndroidX là chúng được xây dựng để hoạt động cho cả thử nghiệm cục bộ và thử nghiệm đo lường. Điều này rất tốt vì:
- Bạn có thể chạy thử nghiệm tương tự như thử nghiệm cục bộ hoặc thử nghiệm đo lường.
- Bạn không cần phải tìm hiểu các API thử nghiệm khác nhau dành cho thử nghiệm cục bộ so với thử nghiệm đo lường.
Ví dụ: vì bạn đã viết mã bằng thư viện Thử nghiệm AndroidX nên bạn có thể di chuyển lớp TasksViewModelTest
từ thư mục test
sang thư mục androidTest
và các thử nghiệm sẽ vẫn chạy. getApplicationContext()
hoạt động hơi khác nhau tùy thuộc vào việc thử nghiệm đó đang được chạy dưới dạng thử nghiệm cục bộ hay thử nghiệm:
- Nếu thử nghiệm được đo lường, nó sẽ nhận được ngữ cảnh Ứng dụng thực tế được cung cấp khi khởi động trình mô phỏng hoặc kết nối với thiết bị thực.
- Nếu đây là một thử nghiệm cục bộ, thì thử nghiệm này sẽ sử dụng một môi trường Android mô phỏng.
Robolectric là gì?
Môi trường Android mô phỏng mà Thử nghiệm AndroidX sử dụng cho các thử nghiệm cục bộ do Robolectric cung cấp. Robolectric là một thư viện tạo ra môi trường Android mô phỏng cho mục đích thử nghiệm và chạy nhanh hơn việc khởi động trình mô phỏng hoặc chạy trên thiết bị. Nếu không có phần phụ thuộc Robolectric, bạn sẽ gặp lỗi sau:
Việc @RunWith(AndroidJUnit4::class)
làm gì?
Trình chạy kiểm tra là một thành phần JUnit chạy thử nghiệm. Nếu không có trình chạy thử nghiệm, thử nghiệm của bạn sẽ không chạy. Có một trình chạy thử nghiệm mặc định do JUnit cung cấp mà bạn sẽ nhận được tự động. @RunWith
sẽ thay đổi trình chạy thử nghiệm mặc định đó.
Trình chạy thử nghiệm AndroidJUnit4
cho phép Thử nghiệm AndroidX chạy thử nghiệm theo cách khác nhau, tùy thuộc vào việc thử nghiệm đó là phương tiện thử nghiệm hay thử nghiệm trên thiết bị.
Bước 6. Khắc phục cảnh báo cướp biển
Khi bạn chạy mã, hãy lưu ý rằng Robolectric được sử dụng.
Nhờ có tính năng Thử nghiệm AndroidX và người chạy thử nghiệm AndroidJunit4, bạn có thể thực hiện việc này mà không cần phải trực tiếp viết một dòng mã Robolectric!
Bạn có thể thấy 2 cảnh báo.
No such manifest file: ./AndroidManifest.xml
"WARN: Android SDK 29 requires Java 9..."
Bạn có thể khắc phục cảnh báo No such manifest file: ./AndroidManifest.xml
bằng cách cập nhật tệp gradle của bạn.
- Thêm dòng sau vào tệp gradle của bạn để sử dụng tệp kê khai Android chính xác. Tùy chọn includeAndroidResources cho phép bạn truy cập vào các tài nguyên Android trong thử nghiệm đơn vị, bao gồm cả tệp 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
// ...
}
Cảnh báo "WARN: Android SDK 29 requires Java 9..."
phức tạp hơn. Việc chạy thử nghiệm trên Android Q yêu cầu Java 9. Thay vì cố gắng định cấu hình Android Studio để sử dụng Java 9, đối với lớp học lập trình này, hãy giữ SDK mục tiêu và biên dịch SDK ở mức 28.
Tóm tắt:
- Việc thử nghiệm mô hình chế độ xem thuần túy thường có thể diễn ra trong bộ nguồn
test
vì mã của chúng thường không yêu cầu Android. - Bạn có thể sử dụng thư viện kiểm tra AndroidX để tải phiên bản thử nghiệm của các thành phần như Ứng dụng và Hoạt động.
- Nếu cần chạy mã Android được mô phỏng trong tập hợp nguồn
test
, bạn có thể thêm phần phụ thuộc Robolectric và chú thích@RunWith(AndroidJUnit4::class)
.
Xin chúc mừng, bạn đang sử dụng cả thư viện thử nghiệm AndroidX và Robolectric để chạy thử nghiệm. Bài kiểm tra của bạn chưa hoàn tất (bạn chưa viết tuyên bố xác nhận, bạn chỉ cần nói // TODO test LiveData
). Bạn sẽ học cách viết tuyên bố xác nhận với LiveData
tiếp theo.
Trong nhiệm vụ này, bạn sẽ tìm hiểu cách xác nhận chính xác giá trị LiveData
.
Đây là nơi bạn đã dừng lại mà không kiểm tra mô hình xem 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
}
Để kiểm tra LiveData
, bạn nên làm hai việc sau:
- Sử dụng
InstantTaskExecutorRule
- Đảm bảo chế độ quan sát của
LiveData
Bước 1. Sử dụngQuy tắc thực thi tác vụ tức thì
InstantTaskExecutorRule
là một Quy tắc JUnit. Khi bạn dùng chú thích này với chú thích @get:Rule
, thao tác này sẽ khiến một số mã trong lớp InstantTaskExecutorRule
chạy trước và sau khi thử nghiệm (để xem mã chính xác, bạn có thể dùng phím tắt Command+B để xem tệp).
Quy tắc này chạy tất cả các công việc trong nền Thành phần cấu trúc liên quan trong cùng một luồng để kết quả thử nghiệm diễn ra đồng bộ và theo thứ tự lặp lại. Khi bạn viết thử nghiệm bao gồm LiveData thử nghiệm, hãy sử dụng quy tắc này!
- Thêm phần phụ thuộc gradle cho thư viện thử nghiệm cốt lõi của Thành phần kiến trúc (có chứa quy tắc này).
app/build.gradle
testImplementation "androidx.arch.core:core-testing:$archTestingVersion"
- Mở
TasksViewModelTest.kt
- Hãy thêm
InstantTaskExecutorRule
bên trong lớpTasksViewModelTest
.
TasksViewModelTest.kt
class TasksViewModelTest {
@get:Rule
var instantExecutorRule = InstantTaskExecutorRule()
// Other code...
}
Bước 2. Thêm lớp LiveDataTestUtil.kt
Bước tiếp theo là đảm bảo rằng bạn quan sát được LiveData
.
Khi sử dụng LiveData
, bạn thường có một hoạt động hoặc mảnh (LifecycleOwner
) quan sát LiveData
.
viewModel.resultLiveData.observe(fragment, Observer {
// Observer code here
})
Quan sát này là quan trọng. Bạn cần có các đối tượng tiếp nhận dữ liệu đang hoạt động trên LiveData
để
- kích hoạt bất kỳ sự kiện
onChanged
nào. - kích hoạt bất kỳ Transformations nào.
Để nhận được hành vi LiveData
dự kiến cho mô hình chế độ xem LiveData
, bạn cần quan sát LiveData
bằng LifecycleOwner
.
Điều này gây ra vấn đề: trong thử nghiệm TasksViewModel
, bạn không có hoạt động hay mảnh nào để quan sát LiveData
của mình. Để giải quyết vấn đề này, bạn có thể sử dụng phương thức observeForever
để đảm bảo LiveData
liên tục được quan sát mà không cần LifecycleOwner
. Khi observeForever
, bạn cần nhớ loại bỏ người quan sát hoặc có nguy cơ rò rỉ đối tượng tiếp nhận dữ liệu.
Mã này trông giống như mã bên dưới. Hãy kiểm tra:
@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)
}
}
Đó là rất nhiều mã nguyên mẫu để quan sát một LiveData
duy nhất trong thử nghiệm! Có một số cách để loại bỏ mẫu nguyên mẫu này. Bạn sẽ tạo một hàm mở rộng có tên là LiveDataTestUtil
để việc thêm đối tượng tiếp nhận dữ liệu trở nên đơn giản hơn.
- Tạo một tệp Kotlin mới có tên là
LiveDataTestUtil.kt
trong tập hợp nguồntest
của bạn.
- Sao chép và dán mã bên dưới.
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
}
Đây là một phương thức khá phức tạp. Hàm này tạo ra một hàm mở rộng Kotlin có tên là getOrAwaitValue
. Hàm này thêm một đối tượng tiếp nhận dữ liệu, lấy giá trị LiveData
, sau đó xóa một đối tượng tiếp nhận dữ liệu – về cơ bản là một phiên bản ngắn và có thể tái sử dụng của mã observeForever
hiển thị ở trên. Để xem nội dung giải thích đầy đủ về lớp học này, hãy xem bài đăng trên blog này.
Bước 3. Sử dụng getOrAChờValue để viết khẳng định
Ở bước này, bạn sử dụng phương thức getOrAwaitValue
và viết câu lệnh xác nhận kiểm tra để đảm bảo rằng newTaskEvent
đã được kích hoạt.
- Nhận giá trị
LiveData
chonewTaskEvent
bằnggetOrAwaitValue
.
val value = tasksViewModel.newTaskEvent.getOrAwaitValue()
- Xác nhận rằng giá trị không phải là null.
assertThat(value.getContentIfNotHandled(), (not(nullValue())))
Quá trình kiểm tra hoàn tất phải có dạng như mã bên dưới.
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()))
}
}
- Chạy mã của bạn và xem bài kiểm tra!
Giờ đây, khi đã biết cách viết thử nghiệm, bạn có thể tự viết chúng. Trong bước này, hãy sử dụng các kỹ năng mà bạn đã học được, thực hành viết một bài kiểm tra TasksViewModel
khác.
Bước 1. Viết thử nghiệm ViewModel của riêng bạn
Bạn sẽ viết setFilterAllTasks_tasksAddViewVisible()
. Thử nghiệm này sẽ kiểm tra để đảm bảo rằng nếu bạn đặt loại bộ lọc để hiển thị tất cả việc cần làm, thì nút Thêm việc cần làm sẽ hiển thị.
- Sử dụng
addNewTask_setsNewTaskEvent()
để tham chiếu, viết thử nghiệm trongTasksViewModelTest
có tênsetFilterAllTasks_tasksAddViewVisible()
, đặt chế độ lọc thànhALL_TASKS
và xác nhận rằng LiveDatatasksAddViewVisible
làtrue
.
Hãy sử dụng mã bên dưới để bắt đầu.
TasksViewModelTest
@Test
fun setFilterAllTasks_tasksAddViewVisible() {
// Given a fresh ViewModel
// When the filter type is ALL_TASKS
// Then the "Add task" action is visible
}
Lưu ý:
- liệt kê
TasksFilterType
cho tất cả việc cần làm làALL_TASKS.
- Chế độ hiển thị của nút để thêm một việc cần làm là do
LiveData
tasksAddViewVisible.
kiểm soát
- Chạy thử nghiệm.
Bước 2. So sánh thử nghiệm của bạn với giải pháp
So sánh giải pháp của bạn với giải pháp bên dưới.
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))
}
Kiểm tra xem bạn có làm những việc sau:
- Bạn tạo
tasksViewModel
bằng cách sử dụng cùng một câu lệnhApplicationProvider.getApplicationContext()
X trên AndroidX. - Bạn gọi phương thức
setFiltering
, chuyển vào loại bộ lọcALL_TASKS
enum. - Bạn kiểm tra xem
tasksAddViewVisible
có đúng không, bằng phương thứcgetOrAwaitNextValue
.
Bước 3. Thêm quy tắc @Before
Hãy xem cách bạn xác định TasksViewModel
khi bắt đầu cả hai thử nghiệm.
TasksViewModelTest
// Given a fresh ViewModel
val tasksViewModel = TasksViewModel(ApplicationProvider.getApplicationContext())
Khi thiết lập lặp lại mã cho nhiều thử nghiệm, bạn có thể dùng chú thích @Before để tạo một phương thức thiết lập và xoá mã lặp lại. Vì tất cả những thử nghiệm này sẽ thử nghiệm TasksViewModel
và cần có mô hình chế độ xem, nên hãy chuyển mã này sang khối @Before
.
- Tạo một biến thực thể
lateinit
có tên làtasksViewModel|
. - Tạo một phương thức có tên là
setupViewModel
. - Chú thích bằng
@Before
. - Di chuyển mã tạo thực thể chế độ xem sang
setupViewModel
.
TasksViewModelTest
// Subject under test
private lateinit var tasksViewModel: TasksViewModel
@Before
fun setupViewModel() {
tasksViewModel = TasksViewModel(ApplicationProvider.getApplicationContext())
}
- Chạy mã!
Cảnh báo
Không thực hiện những việc sau, không khởi tạo
tasksViewModel
kèm theo định nghĩa:
val tasksViewModel = TasksViewModel(ApplicationProvider.getApplicationContext())
Điều này sẽ khiến cùng một trường hợp được sử dụng cho tất cả thử nghiệm. Đây là nội dung bạn nên tránh vì mỗi lần thử nghiệm phải có một thực thể mới của chủ thể đang được thử nghiệm (trong trường hợp này là ViewModel).
Mã cuối cùng của bạn cho TasksViewModelTest
phải giống như mã bên dưới.
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))
}
}
Nhấp vào đây để xem sự khác biệt giữa mã bạn đã bắt đầu và mã cuối cùng.
Để tải xuống mã cho lớp học lập trình đã hoàn thành, bạn có thể sử dụng lệnh git bên dưới:
$ git clone https://github.com/googlecodelabs/android-testing.git $ cd android-testing $ git checkout end_codelab_1
Hoặc bạn có thể tải kho lưu trữ xuống dưới dạng tệp Zip, giải nén và mở tệp đó trong Android Studio.
Lớp học lập trình này đề cập đến:
- Cách chạy thử nghiệm từ Android Studio.
- Sự khác biệt giữa các bài kiểm tra cục bộ (
test
) và các thiết bị đo lường (androidTest
). - Cách viết bài kiểm tra đơn vị cục bộ bằng JUnit và Hamcrest.
- Thiết lập thử nghiệm ViewModel với Thư viện thử nghiệm AndroidX.
Khóa học từ Udacity:
Tài liệu dành cho nhà phát triển Android:
- Hướng dẫn về cấu trúc ứng dụng
- JUnit 4
- Hamcrest
- Thư viện Thử nghiệm Robolectric
- Thư viện thử nghiệm AndroidX
- Thư viện thử nghiệm cốt lõi của các thành phần cấu trúc AndroidX
- nhóm nguồn
- Kiểm tra từ dòng lệnh
Video:
Các tài liệu khác:
Để xem đường liên kết đến các lớp học lập trình khác trong khóa học này, hãy xem trang đích Nâng cao cho Android trong Kotlin.