Kiểm thử mảnh

Chủ đề này mô tả cách đưa API do khung (framework) cung cấp vào kiểm thử để đánh giá hành vi của từng mảnh.

Mảng đóng vai trò là vùng chứa có thể sử dụng lại trong ứng dụng, cho phép bạn trình bày bố cục giao diện người dùng giống nhau trong nhiều hoạt động và cấu hình bố cục khác nhau. Do mảnh có bản chất linh hoạt, điều quan trọng là xác thực rằng các mảnh đó mang lại trải nghiệm nhất quán và hiệu quả về tài nguyên. Lưu ý những điều sau:

  • Mảnh không nên phụ thuộc vào một hoạt động cha hoặc mảnh cụ thể nào đó.
  • Bạn không nên tạo hệ phân cấu khung nhìn của mảnh trừ khi người dùng có thể tương tác được với mảnh đó.

Để hỗ trợ thiết lập các điều kiện nhằm thực hiện các chương trình kiểm thử này, thư viện AndroidX fragment-testing cung cấp lớp FragmentScenario để tạo các mảnh và thay đổi Lifecycle.State.

Khai báo phần phụ thuộc

Để sử dụng FragmentScenario, hãy xác định cấu phần phần mềm fragment-testing-manifest trong tệp build.gradle của ứng dụng bằng debugImplementation và cấu phần phần mềm fragment-testing sử dụng androidTestImplementation như minh hoạ trong ví dụ sau:

Groovy

dependencies {
    def fragment_version = "1.8.3"

    debugImplementation "androidx.fragment:fragment-testing-manifest:$fragment_version"

    androidTestImplementation "androidx.fragment:fragment-testing:$fragment_version"
}

Kotlin

dependencies {
    val fragment_version = "1.8.3"

    debugImplementation("androidx.fragment:fragment-testing-manifest:$fragment_version")

    androidTestImplementation("androidx.fragment:fragment-testing:$fragment_version")
}

Các ví dụ kiểm thử trên trang này sử dụng câu nhận định trong các thư viện EspressoTruth. Để biết thông tin về các thư viện câu nhận định và kiểm thử hiện có khác, hãy xem bài viết Thiết lập dự án cho AndroidX Test.

Tạo mảnh

FragmentScenario bao gồm các phương thức sau để chạy các mảnh khi kiểm thử:

  • launchInContainer() để kiểm thử giao diện người dùng của một mảnh. FragmentScenario đính kèm mảnh vào trình điều khiển khung nhìn gốc (root view controller) của hoạt động. Nếu không thì hoạt động chứa này là rỗng.
  • launch() để kiểm thử mà không cần giao diện người dùng của mảnh. FragmentScenario đính kèm loại mảnh này vào hoạt động rỗng mà không có khung nhìn gốc.

Sau khi khởi chạy một trong những loại mảnh này, FragmentScenario sẽ chuyển mảnh đang được kiểm thử sang một trạng thái đã chỉ định. Theo mặc định, trạng thái này là RESUMED nhưng bạn có thể ghi đè trạng thái này bằng đối số initialState. Trạng thái RESUMED cho biết mảnh này đang chạy và người dùng có thể nhìn thấy nó. Bạn có thể đánh giá thông tin về các thành phần trên giao diện người dùng của mảnh bằng cách sử dụng chương trình kiểm thử giao diện người dùng Espresso.

Các mã ví dụ sau đây minh hoạ cách khởi chạy mảnh theo từng phương thức:

Ví dụ về launchInContainer()

@RunWith(AndroidJUnit4::class)
class MyTestSuite {
    @Test fun testEventFragment() {
        // The "fragmentArgs" argument is optional.
        val fragmentArgs = bundleOf(selectedListItem to 0)
        val scenario = launchFragmentInContainer<EventFragment>(fragmentArgs)
        ...
    }
}

Ví dụ về launch()

@RunWith(AndroidJUnit4::class)
class MyTestSuite {
    @Test fun testEventFragment() {
        // The "fragmentArgs" arguments are optional.
        val fragmentArgs = bundleOf("numElements" to 0)
        val scenario = launchFragment<EventFragment>(fragmentArgs)
        ...
    }
}

Cung cấp các phần phụ thuộc

Nếu các mảnh có các phần phụ thuộc, bạn có thể cung cấp phiên bản kiểm thử của các phần phụ thuộc đó bằng cách cung cấp một FragmentFactory tuỳ chỉnh cho phương thức launchInContainer() hoặc launch().

@RunWith(AndroidJUnit4::class)
class MyTestSuite {
    @Test fun testEventFragment() {
        val someDependency = TestDependency()
        launchFragmentInContainer {
            EventFragment(someDependency)
        }
        ...
    }
}

Để biết thêm thông tin về cách sử dụng FragmentFactory để cung cấp các phần phụ thuộc cho các mảnh, hãy xem Trình quản lý mảnh.

Chuyển mảnh sang trạng thái mới

Trong các chương trình kiểm thử giao diện người dùng của ứng dụng, thông thường, bạn chỉ cần chạy mảnh cần kiểm thử và bắt đầu kiểm thử mảnh đó trong trạng thái RESUMED. Tuy nhiên, khi kiểm thử đơn vị chi tiết, bạn cũng có thể đánh giá hành vi của mảnh khi chuyển đổi từ trạng thái vòng đời này sang trạng thái vòng đời khác. Bạn có thể chỉ định trạng thái ban đầu bằng cách truyền đối số initialState đến bất kỳ hàm launchFragment*() nào.

Để chuyển mảnh sang trạng thái vòng đời khác, hãy gọi moveToState(). Phương thức này hỗ trợ các trạng thái sau đây dưới dạng đối số: CREATED, STARTED, RESUMEDDESTROYED. Phương thức này mô phỏng một tình huống mà trong đó mảnh hoặc hoạt động chứa mảnh thay đổi trạng thái vì bất kỳ lý do nào.

Ví dụ sau đây sẽ khởi chạy một mảnh kiểm thử trong trạng thái INITIALIZED, sau đó chuyển mảnh đó sang trạng thái RESUMED như sau:

@RunWith(AndroidJUnit4::class)
class MyTestSuite {
    @Test fun testEventFragment() {
        val scenario = launchFragmentInContainer<EventFragment>(
            initialState = Lifecycle.State.INITIALIZED
        )
        // EventFragment has gone through onAttach(), but not onCreate().
        // Verify the initial state.
        scenario.moveToState(Lifecycle.State.RESUMED)
        // EventFragment moves to CREATED -> STARTED -> RESUMED.
        ...
    }
}

Tạo lại mảnh

Nếu ứng dụng đang chạy trên một thiết bị sắp hết tài nguyên, thì hệ thống có thể huỷ bỏ hoạt động có chứa mảnh. Trường hợp này sẽ yêu cầu ứng dụng của bạn tạo lại mảnh khi người dùng quay lại. Để mô phỏng trường hợp này, hãy gọi recreate():

@RunWith(AndroidJUnit4::class)
class MyTestSuite {
    @Test fun testEventFragment() {
        val scenario = launchFragmentInContainer<EventFragment>()
        scenario.recreate()
        ...
    }
}

FragmentScenario.recreate() huỷ mảnh và máy chủ lưu trữ của mảnh rồi tạo lại. Khi lớp FragmentScenario tạo lại mảnh đang được kiểm thử, mảnh sẽ trở lại trạng thái vòng đời trước khi bị huỷ.

Tương tác với các mảnh giao diện người dùng

Để kích hoạt hành động trên giao diện người dùng trong mảnh đang được kiểm thử, hãy sử dụng trình so khớp khung nhìn Espresso để tương tác với các thành phần trong khung nhìn:

@RunWith(AndroidJUnit4::class)
class MyTestSuite {
    @Test fun testEventFragment() {
        val scenario = launchFragmentInContainer<EventFragment>()
        onView(withId(R.id.refresh)).perform(click())
        // Assert some expected behavior
        ...
    }
}

Nếu cần gọi một phương thức cho chính mảnh đó, chẳng hạn như phản hồi một lựa chọn trong trình đơn tuỳ chọn, bạn có thể thực hiện lệnh gọi một cách an toàn bằng cách tham chiếu đến mảnh bằng cách sử dụng FragmentScenario.onFragment() và truyền vào FragmentAction như sau:

@RunWith(AndroidJUnit4::class)
class MyTestSuite {
    @Test fun testEventFragment() {
        val scenario = launchFragmentInContainer<EventFragment>()
        scenario.onFragment { fragment ->
            fragment.myInstanceMethod()
        }
    }
}

Kiểm thử thao tác hộp thoại

FragmentScenario cũng hỗ trợ việc kiểm thử các mảnh hộp thoại. Mặc dù các mảnh hộp thoại có các thành phần trên giao diện người dùng, nhưng bố cục của các mảnh này được điền sẵn trong một cửa sổ riêng chứ không phải trong hoạt động. Chính vì vậy mà hãy sử dụng FragmentScenario.launch() để kiểm thử các mảnh hộp thoại.

Ví dụ sau đây kiểm thử quy trình loại bỏ hộp thoại:

@RunWith(AndroidJUnit4::class)
class MyTestSuite {
    @Test fun testDismissDialogFragment() {
        // Assumes that "MyDialogFragment" extends the DialogFragment class.
        with(launchFragment<MyDialogFragment>()) {
            onFragment { fragment ->
                assertThat(fragment.dialog).isNotNull()
                assertThat(fragment.requireDialog().isShowing).isTrue()
                fragment.dismiss()
                fragment.parentFragmentManager.executePendingTransactions()
                assertThat(fragment.dialog).isNull()
            }
        }

        // Assumes that the dialog had a button
        // containing the text "Cancel".
        onView(withText("Cancel")).check(doesNotExist())
    }
}