Thử nghiệm thành phần điều hướng

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

Trong các lớp học lập trình trước, bạn đã tìm hiểu cách điều hướng bằng Thành phần điều hướng (Navigation Component). Trong lớp học lập trình này, bạn sẽ tìm hiểu cách kiểm thử các Thành phần điều hướng. Xin lưu ý rằng quá trình này khác với việc thử nghiệm hoạt động điều hướng mà không sử dụng thành phần điều hướ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 sử dụng bài kiểm thử đo lường để kiểm thử thành phần điều hướng.
  • Cách thiết lập kiểm thử mà mã không bị lặp lại.

Bạn cần có

  • Một máy tính đã cài đặt Android Studio.
  • Mã giải pháp cho ứng dụng Words (Từ vựng).

Tải mã khởi đầu 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 bài kiểm thử đo lường vào mã giải pháp cho ứng dụng Words (Từ vựng).

  1. Chuyển đến trang kho lưu trữ GitHub được cung cấp cho dự án.
  2. Xác minh rằng 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à main.

1e4c0d2c081a8fd2.png

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

1debcf330fd04c7b.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), hãy nhấp vào Open (Mở).

d8e9dbdeafe9038a.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.

8d1fda7396afe8e5.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 (thường 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) 8de56cba7583251f.png để xây 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 đầu

Ứng dụng Words (Từ vựng) bao gồm một màn hình chính cho thấy một danh sách, mỗi mục trong danh sách là một chữ cái của bảng chữ cái. Khi nhấp vào một chữ cái, người dùng sẽ được chuyển đến một màn hình cho thấy một danh sách các từ bắt đầu bằng chữ cái đó.

3. Tạo thư mục kiểm thử

Nếu cần, hãy tạo một thư mục kiểm thử đo lường cho ứng dụng Words như bạn đã thực hiện trong các lớp học lập trình trước. Nếu đã thực hiện việc này, bạn có thể bỏ qua để chuyển sang bước Thêm phần phụ thuộc cần thiết.

4. Tạo lớp kiểm thử đo lường

Tạo một lớp mới có tên NavigationTests.kt trong thư mục androidTest.

b023023a2ccc3813.png

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

Việc kiểm thử các thành phần điều hướng đòi hỏi một số phần phụ thuộc Gradle cụ thể. Chúng ta cũng sẽ thêm một phần phụ thuộc để kiểm thử các mảnh (fragment) theo một cách rất cụ thể. Chuyển đến tệp build.gradle của mô-đun ứng dụng rồi thêm phần phụ thuộc sau:

androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
androidTestImplementation 'com.android.support.test.espresso:espresso-contrib:3.4.0'
androidTestImplementation 'androidx.navigation:navigation-testing:2.5.2'

debugImplementation 'androidx.fragment:fragment-testing:1.5.3'

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

6. Viết mã kiểm thử Thành phần điều hướng

Việc kiểm thử Thành phần điều hướng khác với việc kiểm thử điều hướng thông thường. Khi kiểm thử điều hướng thông thường, chúng ta sẽ kích hoạt thao tác điều hướng để thực thi trên thiết bị hoặc trình mô phỏng. Khi kiểm thử Thành phần điều hướng, chúng ta không thực sự làm cho thiết bị hoặc trình mô phỏng điều hướng rõ ràng. Thay vào đó, chúng ta buộc trình điều khiển điều hướng di chuyển mà không thực sự thay đổi nội dung xuất hiện trên thiết bị hoặc trình mô phỏng. Sau đó, chúng ta kiểm tra để đảm bảo rằng trình điều khiển điều hướng đến đúng đích đến.

  1. Tạo một hàm kiểm thử có tên navigate_to_words_nav_component().
  2. Khi xử lý Thành phần điều hướng trong các bài kiểm thử, bạn cần thiết lập một số tuỳ chọn. Trong phương thức navigate_to_words_nav_component(), hãy tạo một thực thể kiểm thử của trình điều khiển điều hướng.
val navController = TestNavHostController(
   ApplicationProvider.getApplicationContext()
)
  1. Thành phần điều hướng hỗ trợ giao diện người dùng bằng cách sử dụng các Mảnh. Có một mảnh tương đương với ActivityScenarioRule có thể được dùng để cô lập một mảnh nhằm mục đích kiểm thử, đó là lý do phải cần có phần phụ thuộc dành riêng cho mảnh. Việc cô lập có thể rất hữu ích cho quá trình kiểm thử một mảnh cần đến nhiều bước điều hướng để tiếp cận, vì mảnh đó có thể được khởi chạy mà không cần mã bổ sung để xử lý việc điều hướng tới mảnh đó.
val letterListScenario = launchFragmentInContainer<LetterListFragment>(themeResId =
R.style.Theme_Words)

Tại đây, chúng ta xác định rõ rằng mình muốn khởi chạy LetterListFragment. Chúng ta phải truyền giao diện của ứng dụng để các thành phần giao diện người dùng biết được cần sử dụng giao diện nào, nếu không thì bài kiểm thử có thể gặp sự cố.

  1. Cuối cùng, chúng ta cần phải khai báo rõ ràng rằng chúng ta muốn trình điều khiển điều hướng (nav controller) sử dụng biểu đồ điều hướng (navigation graph) nào cho mảnh được khởi chạy.
letterListScenario.onFragment { fragment ->

   navController.setGraph(R.navigation.nav_graph)

   Navigation.setViewNavController(fragment.requireView(), navController)
}
  1. Bây giờ, hãy kích hoạt sự kiện sẽ nhắc việc điều hướng.
onView(withId(R.id.recycler_view))
   .perform(RecyclerViewActions
       .actionOnItemAtPosition<RecyclerView.ViewHolder>(2, click()))

Khi sử dụng phương thức launchFragmentInContainer(), bạn không thể điều hướng vì vùng chứa không biết đến các mảnh hoặc hoạt động khác mà chúng ta có thể đang điều hướng đến. Vùng chứa chỉ biết đến mảnh mà chúng ta đã chỉ định để khởi chạy trong đó. Do đó, khi chạy bài kiểm thử này trên một thiết bị hoặc trình mô phỏng, bạn sẽ không thấy chế độ điều hướng thực tế nào. Mặc dù có vẻ không trực quan, nhưng điều này cho phép chúng ta đưa ra nhận định trực tiếp hơn về đích đến hiện tại. Thay vì tìm một thành phần giao diện người dùng mà chúng ta biết sẽ hiển thị trên một màn hình cụ thể, chúng ta có thể kiểm tra để đảm bảo rằng đích đến của trình điều khiển điều hướng hiện tại có mã nhận dạng của mảnh mà chúng ta mong muốn hiển thị. Phương pháp này đáng tin cậy hơn đáng kể so với phương pháp trên.

assertEquals(navController.currentDestination?.id, R.id.wordListFragment)

Bài kiểm thử của bạn sẽ có dạng như sau: 78b4a72f75134ded.png

7. Mã giải pháp

8. Dùng chú thích để tránh lặp lại mã

Trong Android, cả các bài kiểm thử đo lường lẫn các bài kiểm thử đơn vị đều có tính năng cho phép chúng ta thiết lập cùng một cấu hình cho mọi bài kiểm thử trong một lớp mà không cần lặp lại mã.

Ví dụ: hãy giả định rằng chúng ta có một mảnh có 10 nút (button). Mỗi nút sẽ dẫn đến một mảnh riêng biệt khi được nhấp vào.

Nếu làm theo mẫu trong bài kiểm thử ở trên, chúng ta sẽ phải lặp lại mã tương tự thế này đối với mỗi bài kiểm thử trong số 10 bài (lưu ý rằng mã này chỉ là một ví dụ và sẽ không được biên dịch trong ứng dụng mà chúng ta sử dụng trong lớp học lập trình này):

val navController = TestNavHostController(
    ApplicationProvider.getApplicationContext()
)

val exampleFragmentScenario = launchFragmentInContainer<ExampleFragment>(themeResId =
R.style.Theme_Example)

exampleFragmentScenario.onFragment { fragment ->

   navController.setGraph(R.navigation.example_nav_graph)

   Navigation.setViewNavController(fragment.requireView(), navController)
}

Bạn sẽ có rất nhiều mã lặp lại 10 lần. Tuy nhiên, trong trường hợp này, chúng ta có thể tiết kiệm thời gian bằng cách sử dụng chú thích @Before do JUnit cung cấp. Chúng ta sử dụng phương pháp này bằng cách chú thích một phương thức mà sau đó chúng ta cung cấp mã cần thiết để thiết lập bài kiểm thử. Chúng ta có thể đặt tên bất kỳ cho phương thức đó (nhưng nên có liên quan). Thay vì thiết lập cùng một mảnh 10 lần, chúng ta có thể viết mã thiết lập một lần như sau:

lateinit var navController: TestNavHostController

lateinit var exampleFragmentScenario: FragmentScenario<ExampleFragment>

@Before
fun setup(){
    navController = TestNavHostController(
        ApplicationProvider.getApplicationContext()
    )

    exampleFragmentScenario =  launchFragmentInContainer(themeResId=R.style.Theme_Example)

    exampleFragmentScenario.onFragment { fragment ->

       navController.setGraph(R.navigation.example_nav_graph)

       Navigation.setViewNavController(fragment.requireView(),  navController)
    }
}

Phương thức này hiện dùng được trên mọi bài kiểm thử mà chúng ta viết trong lớp (class) này, và chúng ta có thể truy cập các biến cần thiết qua bài kiểm thử bất kỳ.

Tương tự, nếu có mã mà chúng ta cần thực thi sau mỗi bài kiểm thử, chúng ta có thể sử dụng chú thích @After. Ví dụ: @After có thể được dùng để dọn dẹp tài nguyên mà chúng ta đã dùng cho bài kiểm thử của mình hoặc cho các bài kiểm thử đo lường. Chúng ta có thể sử dụng chú thích này để đưa thiết bị về một trạng thái cụ thể.

JUnit cũng cung cấp các chú thích @BeforeClass@AfterClass. Điểm khác biệt ở đây là các phương thức có chú thích này được thực thi một lần, nhưng mã được thực thi vẫn áp dụng cho mọi phương thức. Nếu việc thiết lập hoặc chia nhỏ phương thức gây tốn kém tài nguyên hệ thống, bạn nên sử dụng các chú thích này. Các phương thức được chú thích bằng @BeforeClass@AfterClass phải được đặt trong một đối tượng đồng hành và có chú thích @JvmStatic. Để minh hoạ thứ tự thực thi của các chú thích này, hãy xem mã sau:

5157ab00a9b7fb84.png

Hãy nhớ rằng @BeforeClass chạy cho lớp (class), @Before chạy trước các hàm, @After chạy sau các hàm và @AfterClass chạy cho lớp. Bạn có thể đoán được đầu ra của đoạn mã này sẽ như thế nào không?

39c04aa2ba7b8348.png

Thứ tự thực thi của các hàm là setupClass(), setupFunction(), test_a(), tearDownFunction(), setupFunction(), test_b(), tearDownFunction(), setupFunction(), test_c(), tearDownFunction(), tearDownClass(). Thứ tự này là hợp lý vì @Before@After chạy trước và sau mỗi phương thức, theo thứ tự tương ứng. @BeforeClass chạy một lần trước khi các hàm khác trong lớp chạy và @AfterClass chạy một lần sau khi mọi hàm khác trong lớp đã chạy.

9. Xin chúc mừng

Trong lớp học lập trình này, bạn đã:

  • Đã tìm hiểu cách kiểm tra Thành phần điều hướng.
  • Tìm hiểu cách tránh lặp lại mã nhờ các chú thích @Before, @BeforeClass, @After@AfterClass.