Kiểm thử ViewModels và LiveData

1. Trước khi bắt đầu

Trong các lớp học lập trình trước đây, bạn đã tìm hiểu cách sử dụng ViewModel để xử lý logic kinh doanh, cũng như LiveData cho giao diện người dùng phản ứng. Trong lớp học lập trình này, bạn sẽ tìm hiểu cách viết mã kiểm thử đơn vị để kiểm tra xem mã ViewModel của bạn có chạy tốt hay không.

Điều kiện tiên quyết

  • Bạn đã tạo thư mục kiểm thử trong Android Studio.
  • Bạn đã viết mã kiểm thử đơn vị và kiểm thử đo lường trong Android Studio.
  • Bạn đã thêm phần phụ thuộc Gradle vào một dự án Android.

Kiến thức bạn sẽ học được

  • Cách viết mã kiểm thử đơn vị cho ViewModelLiveData.

Bạn cần có

  • Máy tính đã cài đặt Android Studio.
  • Mã giải pháp cho ứng dụng Cupcake.

Tải mã khởi động xuống cho lớp học lập trình này

Trong lớp học lập trình này, bạn sẽ thêm các mã kiểm thử đo lường vào ứng dụng Cupcake từ mã giải pháp trước đó.

Để lấy đoạn mã cho lớp học lập trình này và mở đoạn mã đó trong Android Studio, hãy thực hiện các bước sau.

Lấy mã

  1. Nhấp vào URL được cung cấp. Thao tác này sẽ mở trang GitHub của dự án trong một trình duyệt.
  2. Kiểm tra để đảm bảo tên nhánh khớp với tên nhánh được chỉ định trong lớp học lập trình. Ví dụ: trong ảnh chụp màn hình sau đây, tên nhánh là chính.

fe29aa9112862a93.png

  1. Trên trang GitHub cho dự án này, nhấp vào nút . Thao tác này sẽ khiến một cửa sổ bật lên.

5b0a76c50478a73f.png

  1. Trong cửa sổ bật lên, nhấp vào nút Download ZIP (Tải tệp ZIP xuống) để lưu dự án vào máy tính. Chờ quá trình tải xuống hoàn tất.
  2. Xác định vị trí của tệp trên máy tính (thường nằm trong thư mục Downloads (Tệp đã tải xuống)).
  3. Nhấp đúp vào tệp ZIP để giải nén. Thao tác này sẽ tạo một thư mục mới chứa các tệp dự án.

Mở dự án trong Android Studio

  1. Khởi động Android Studio.
  2. Trong cửa sổ Welcome to Android Studio (Chào mừng bạn đến với Android Studio), nhấp vào Open (Mở).

a065e3d575fe607b.png

Lưu ý: Nếu Android Studio đã mở thì chuyển sang chọn tuỳ chọn File (Tệp) > Open (Mở) trong trình đơn.

4f3b1e628c7695f1.png

  1. Trong trình duyệt tệp, hãy chuyển đến vị trí của thư mục dự án chưa giải nén (có thể nằm trong thư mục Downloads (Tệp đã tải xuống)).
  2. Nhấp đúp vào thư mục dự án đó.
  3. Chờ Android Studio mở dự án.
  4. Nhấp vào nút Run (Chạy) 11c34fc5e516fb1c.png để tạo bản dựng và chạy ứng dụng. Đảm bảo ứng dụng được xây dựng như mong đợi.

2. Tổng quan về ứng dụng khởi động

Ứng dụng Cupcake có màn hình chính hiện một màn hình đặt hàng với 3 lựa chọn về số lượng cupcake. Khi nhấp vào một tuỳ chọn, bạn sẽ được chuyển đến màn hình để chọn hương vị, rồi tiếp đó chuyển đến màn hình để chọn ngày đến lấy bánh. Sau đó, bạn có thể gửi đơn hàng đến một ứng dụng khác. Bạn có thể huỷ đơn đặt hàng ở bất kỳ giai đoạn nào vừa nêu.

3. Tạo thư mục kiểm thử đơn vị

Tạo thư mục kiểm thử đơn vị cho ứng dụng Cupcake như bạn đã làm trong các lớp học lập trình trước đó.

4. Tạo lớp kiểm thử đơn vị

Tạo một lớp mới với tên gọi ViewModelTests.kt.

5. Thêm các phần phụ thuộc cần thiết

Thêm các phần phụ thuộc sau vào dự án của bạn:

testImplementation 'junit:junit:4.+'
testImplementation 'androidx.arch.core:core-testing:2.1.0'

Bây giờ, hãy đồng bộ hoá dự án của bạn.

6. Viết mã kiểm thử ViewModel

Hãy bắt đầu bằng một mã kiểm thử đơn giản. Việc đầu tiên chúng ta làm khi tương tác với ứng dụng trên một thiết bị hoặc trình mô phỏng là chọn số lượng cupcake. Cho nên, trước hết chúng ta sẽ kiểm thử phương thức setQuantity() trong OrderViewModel và kiểm tra giá trị của đối tượng quantity LiveData.

Chúng ta sẽ kiểm thử biến quantity, đó là một thực thể của LiveData. Việc kiểm thử đối tượng LiveData cần có thêm một bước và đây chính là nơi phần phụ thuộc được thêm vào phát huy tác dụng. Chúng ta dùng LiveData để cập nhật giao diện người dùng ngay khi giá trị thay đổi. Giao diện người dùng của chúng ta chạy trên điều chúng ta gọi là "chuỗi chính". Đừng lo nếu bạn không quen với việc tạo chuỗi và tính năng đồng thời, chúng ta sẽ tìm hiểu sâu hơn về vấn đề này trong các lớp học lập trình khác. Đến đây, hãy coi chuỗi chính là chuỗi giao diện người dùng trong ứng dụng Android. Mã hiển thị giao diện người dùng đến người dùng chạy trên chuỗi này. Trừ phi có quy định khác, mã kiểm thử đơn vị giả định rằng mọi thứ đều chạy trên chuỗi chính. Tuy nhiên, vì các đối tượng LiveData không thể truy cập vào chuỗi chính, nên chúng ta phải tuyên bố rõ rằng các đối tượng LiveData không được gọi chuỗi chính.

  1. Để xác định rõ đối tượng LiveData không được gọi chuỗi chính, cần cung cấp một quy tắc kiểm thử cụ thể bất cứ khi nào chúng ta kiểm thử một đối tượng LiveData.
@get:Rule
var instantTaskExecutorRule = InstantTaskExecutorRule()
  1. Giờ đây, chúng ta có thể tạo một hàm có tên là quantity_twelve_cupcakes(). Trong phương thức này, tạo một thực thể của OrderViewModel.
  2. Trong kiểm thử này, bạn sẽ kiểm tra để bảo đảm rằng đối tượng quantity trong OrderViewModel được cập nhật khi setQuantity được gọi. Nhưng trước khi gọi bất kỳ phương thức nào hoặc làm việc với bất kỳ dữ liệu nào trong OrderViewModel, điều quan trọng cần lưu ý là khi kiểm thử giá trị của đối tượng LiveData, bạn cần phải quan sát các đối tượng để phát hiện thay đổi. Cách đơn giản để thực hiện việc này là sử dụng phương thức observeForever. Gọi phương thức observeForever trên đối tượng quantity. Phương thức này đòi hỏi một biểu thức lambda, nhưng có thể để trống.
  3. Tiếp đó, gọi phương thức setQuantity(), truyền vào 12 dưới dạng thông số.
val viewModel = OrderViewModel()
viewModel.quantity.observeForever {}
viewModel.setQuantity(12)
  1. Chúng ta có thể yên tâm suy diễn rằng giá trị của đối tượng quantity12. Lưu ý rằng đối tượng LiveData không phải là chính bản thân giá trị đó. Các giá trị được chứa trong một thuộc tính được gọi là value. Đưa ra nhận định sau:
assertEquals(12, viewModel.quantity.value)

Kiểm thử của bạn sẽ có dạng như sau:

@Test
fun quantity_twelve_cupcakes() {
   val viewModel = OrderViewModel()
   viewModel.quantity.observeForever {}
   viewModel.setQuantity(12)
   assertEquals(12, viewModel.quantity.value)
}

Chạy kiểm thử! Xin chúc mừng, bạn vừa viết mã kiểm thử đơn vị LiveData đầu tiên. Đây là một kỹ năng quan trọng trong việc phát triển Android hiện đại. Kiểm thử này không thử nghiệm nhiều logic kinh doanh, vì vậy, hãy viết mã kiểm thử tương tác nhiều hơn một chút.

Một trong những chức năng chính của OrderViewModel là tính giá tiền cho đơn đặt hàng của chúng ta. Đó là khi chúng ta chọn số lượng cupcake và ngày nhận hàng. Phép tính giá tiền được thực hiện trong một phương thức riêng tư, do vậy, mã kiểm thử của chúng ta không thể trực tiếp gọi phương thức này ra. Chỉ các phương thức khác trong OrderViewModel mới có thể gọi lệnh này. Những phương thức này là công khai, cho nên chúng ta sẽ gọi các phương thức đó để kích hoạt phép tính giá tiền, vì vậy, chúng ta có thể kiểm tra xem giá trị của giá tiền có đúng như dự kiến hay không.

Phương pháp hay nhất

Giá tiền được cập nhật khi số lượng cupcake và ngày được chọn. Dù cả hai chức năng này cần được kiểm thử, nhưng tốt hơn hết chỉ nên kiểm thử một chức năng duy nhất. Do đó, chúng ta sẽ thực hiện riêng từng phương thức cho mỗi kiểm thử: một hàm để kiểm thử giá tiền khi số lượng được cập nhật và một hàm riêng rẽ để kiểm thử giá tiền khi ngày được cập nhật. Chúng ta không bao giờ muốn một kiểm thử không cho ra kết quả vì một kiểm thử khác thất bại.

  1. Tạo một phương thức có tên là price_twelve_cupcakes() rồi chú thích phương thức đó là một kiểm thử.
  2. Trong phương thức này, tạo một thực thể của OrderViewModel và gọi phương thức setQuantity(), truyền trong 12 dưới dạng một thông số.
val viewModel = OrderViewModel()
viewModel.setQuantity(12)
  1. Xem xét PRICE_PER_CUPCAKE trong OrderViewModel, chúng ta thấy rằng đơn giá của cupcake là 2 đô la. Chúng ta cũng thấy rằng resetOrder() được gọi mỗi khi khởi chạy ViewModel và trong phương thức này, ngày mặc định là ngày hôm nay và PRICE_FOR_SAME_DAY_PICKUP là 3 đô la. Do vậy, 12 * 2 + 3 = 27. Chúng ta dự kiến rằng giá trị của biến price sau khi chọn 12 cupcake nhỏ sẽ là 27 đô la. Do vậy, hãy cùng khẳng định rằng giá trị dự kiến là 27 đô la bằng với giá trị của đối tượng price LiveData.
assertEquals("$27.00", viewModel.price.value)

Bây giờ, hãy chạy kiểm thử.

Có lẽ không thành công!

17c8a24e4d7d635d.png

Kết quả kiểm thử cho biết giá trị thực tế của chúng ta là null. Có cách giải thích cho điều này. Nếu xem xét biến price trong OrderViewModel, bạn sẽ thấy:

val price: LiveData<String> = Transformations.map(_price) {
   // Format the price into the local currency and return this as LiveData<String>
   NumberFormat.getCurrencyInstance().format(it)
}

Đây là một ví dụ về lý do tại sao LiveData cần được quan sát trong quá trình kiểm thử. Sử dụng Transformation để xác định giá trị của price. Về cơ bản, mã này sẽ lấy giá trị mà chúng ta gán cho price và chuyển đổi mã đó thành định dạng tiền tệ, nhờ đó ta không phải thực hiện việc này một cách thủ công. Tuy nhiên, mã này còn có các hàm ý khác. Khi chuyển đổi một đối tượng LiveData, mã này sẽ không được gọi trừ phi đây là điều cực kỳ cần thiết, nhờ đó tiết kiệm tài nguyên trên thiết bị di động. Mã sẽ chỉ được gọi nếu chúng ta quan sát thấy có thay đổi ở đối tượng. Tất nhiên, việc này được thực hiện trong ứng dụng của chúng ta, nhưng cũng cần thực hiện điều tương tự cho kiểm thử này.

  1. Trong phương thức kiểm thử của bạn, thêm dòng sau trước khi đặt số lượng:
viewModel.price.observeForever {}

Kiểm thử của bạn sẽ có dạng như sau:

@Test
fun price_twelve_cupcakes() {
   val viewModel = OrderViewModel()
   viewModel.price.observeForever {}
   viewModel.setQuantity(12)
   assertEquals("$27.00", viewModel.price.value)
}

Nếu chạy kiểm thử, bạn sẽ thành công.

7. Mã giải pháp

8. Xin chúc mừng

Trong lớp học lập trình này, chúng ta:

  • Đã tìm hiểu cách thiết lập kiểm thử LiveData.
  • Đã tìm hiểu cách kiểm thử chính bản thân LiveData.
  • Đã tìm hiểu cách kiểm thử LiveData đã thay đổi.
  • Đã tìm hiểu cách quan sát LiveData trong một kiểm thử đơn vị.