Hướng dẫn kiểm thử Hilt

Một trong những lợi ích của việc sử dụng các khung chèn phần phụ thuộc như Hilt là giúp bạn kiểm thử mã dễ dàng hơn.

Kiểm thử đơn vị

Bạn không cần dùng Hilt để kiểm thử đơn vị vì khi kiểm thử một lớp sử dụng tính năng chèn hàm khởi tạo, bạn không cần phải sử dụng Hilt để tạo bản sao của lớp đó. Thay vào đó, bạn có thể trực tiếp gọi một hàm khởi tạo lớp bằng cách truyền vào các phần phụ thuộc giả hoặc mô phỏng, giống như cách bạn sẽ làm nếu hàm khởi tạo không được chú thích:

Kotlin

@ActivityScoped
class AnalyticsAdapter @Inject constructor(
  private val service: AnalyticsService
) { ... }

class AnalyticsAdapterTest {

  @Test
  fun `Happy path`() {
    // You don't need Hilt to create an instance of AnalyticsAdapter.
    // You can pass a fake or mock AnalyticsService.
    val adapter = AnalyticsAdapter(fakeAnalyticsService)
    assertEquals(...)
  }
}

Java

@ActivityScope
public class AnalyticsAdapter {

  private final AnalyticsService analyticsService;

  @Inject
  AnalyticsAdapter(AnalyticsService analyticsService) {
    this.analyticsService = analyticsService;
  }
}

public final class AnalyticsAdapterTest {

  @Test
  public void happyPath() {
    // You don't need Hilt to create an instance of AnalyticsAdapter.
    // You can pass a fake or mock AnalyticsService.
    AnalyticsAdapter adapter = new AnalyticsAdapter(fakeAnalyticsService);
    assertEquals(...);
  }
}

Kiểm thử toàn diện

Đối với thử nghiệm tích hợp, Hilt sẽ chèn các phần phụ thuộc giống như trong mã sản xuất. Việc sử dụng Hilt để kiểm thử không yêu cầu bảo trì vì Hilt tự động tạo một tập hợp các thành phần mới cho mỗi kiểm thử.

Thêm các phần phụ thuộc kiểm thử

Để sử dụng Hilt trong các kiểm thử, hãy đưa phần phụ thuộc hilt-android-testing vào dự án:

Groovy

dependencies {
    // For Robolectric tests.
    testImplementation 'com.google.dagger:hilt-android-testing:2.44'
    // ...with Kotlin.
    kaptTest 'com.google.dagger:hilt-android-compiler:2.44'
    // ...with Java.
    testAnnotationProcessor 'com.google.dagger:hilt-android-compiler:2.44'


    // For instrumented tests.
    androidTestImplementation 'com.google.dagger:hilt-android-testing:2.44'
    // ...with Kotlin.
    kaptAndroidTest 'com.google.dagger:hilt-android-compiler:2.44'
    // ...with Java.
    androidTestAnnotationProcessor 'com.google.dagger:hilt-android-compiler:2.44'
}

Kotlin

dependencies {
    // For Robolectric tests.
    testImplementation("com.google.dagger:hilt-android-testing:2.44")
    // ...with Kotlin.
    kaptTest("com.google.dagger:hilt-android-compiler:2.44")
    // ...with Java.
    testAnnotationProcessor("com.google.dagger:hilt-android-compiler:2.44")


    // For instrumented tests.
    androidTestImplementation("com.google.dagger:hilt-android-testing:2.44")
    // ...with Kotlin.
    kaptAndroidTest("com.google.dagger:hilt-android-compiler:2.44")
    // ...with Java.
    androidTestAnnotationProcessor("com.google.dagger:hilt-android-compiler:2.44")
}

Thiết lập thử nghiệm giao diện người dùng

Bạn phải chú thích mọi kiểm thử giao diện người dùng sử dụng Hilt với @HiltAndroidTest. Chú thích này chịu trách nhiệm tạo các thành phần Hilt cho mỗi lần kiểm thử.

Ngoài ra, bạn cần thêm HiltAndroidRule vào lớp kiểm thử. Công cụ này quản lý trạng thái của các thành phần và được dùng để thực hiện thao tác chèn trong kiểm thử:

Kotlin

@HiltAndroidTest
class SettingsActivityTest {

  @get:Rule
  var hiltRule = HiltAndroidRule(this)

  // UI tests here.
}

Java

@HiltAndroidTest
public final class SettingsActivityTest {

  @Rule
  public HiltAndroidRule hiltRule = new HiltAndroidRule(this);

  // UI tests here.
}

Tiếp theo, kiểm thử cần được biết về lớp Application mà Hilt tự động tạo cho bạn.

Kiểm thử ứng dụng

Bạn phải thực thi các kiểm thử đo lường sử dụng Hilt trong đối tượng Application để hỗ trợ Hilt. Thư viện này cung cấp HiltTestApplication để sử dụng trong kiểm thử. Nếu chương trình kiểm thử của bạn cần một ứng dụng cơ sở khác, vui lòng xem Tùy chỉnh ứng dụng để kiểm thử.

Bạn phải thiết lập ứng dụng kiểm thử để chạy trong các kiểm thử đo lường hoặc kiểm thử Robolectric. Các hướng dẫn sau đây không dành riêng cho Hilt, nhưng là hướng dẫn chung về cách chỉ định một ứng dụng tuỳ chỉnh để chạy trong các lần kiểm thử.

Đặt ứng dụng kiểm thử trong các kiểm thử đo lường

Để sử dụng ứng dụng kiểm thử Hilt trong kiểm thử đo lường, bạn cần định cấu hình một trình chạy kiểm thử mới. Điều này giúp Hilt hoạt động cho tất cả các kiểm thử đo lường trong dự án của bạn. Thực hiện các bước sau đây:

  1. Tạo một lớp tuỳ chỉnh giúp mở rộng AndroidJUnitRunner trong thư mục androidTest.
  2. Ghi đè hàm newApplication và truyền tên của ứng dụng kiểm thử Hilt được tạo.

Kotlin

// A custom runner to set up the instrumented application class for tests.
class CustomTestRunner : AndroidJUnitRunner() {

    override fun newApplication(cl: ClassLoader?, name: String?, context: Context?): Application {
        return super.newApplication(cl, HiltTestApplication::class.java.name, context)
    }
}

Java

// A custom runner to set up the instrumented application class for tests.
public final class CustomTestRunner extends AndroidJUnitRunner {

  @Override
  public Application newApplication(ClassLoader cl, String className, Context context)
      throws ClassNotFoundException, IllegalAccessException, InstantiationException {
    return super.newApplication(cl, HiltTestApplication.class.getName(), context);
  }
}

Tiếp theo, hãy định cấu hình trình chạy kiểm thử này trong tệp Gradle như được mô tả ở phần hướng dẫn kiểm thử đơn vị đo lường. Hãy đảm bảo là bạn sử dụng đường dẫn lớp đầy đủ:

Groovy

android {
    defaultConfig {
        // Replace com.example.android.dagger with your class path.
        testInstrumentationRunner "com.example.android.dagger.CustomTestRunner"
    }
}

Kotlin

android {
    defaultConfig {
        // Replace com.example.android.dagger with your class path.
        testInstrumentationRunner = "com.example.android.dagger.CustomTestRunner"
    }
}
Đặt ứng dụng kiểm thử trong các kiểm thử Robolectric

Nếu sử dụng Robolectric để kiểm thử lớp giao diện người dùng, bạn có thể chỉ định ứng dụng nào sẽ sử dụng trong tệp robolectric.properties:

application = dagger.hilt.android.testing.HiltTestApplication

Ngoài ra, bạn có thể định cấu hình ứng dụng cho từng lần kiểm thử riêng lẻ bằng cách sử dụng chú thích @Config của Robolectric:

Kotlin

@HiltAndroidTest
@Config(application = HiltTestApplication::class)
class SettingsActivityTest {

  @get:Rule
  var hiltRule = HiltAndroidRule(this)

  // Robolectric tests here.
}

Java

@HiltAndroidTest
@Config(application = HiltTestApplication.class)
class SettingsActivityTest {

  @Rule public HiltAndroidRule hiltRule = new HiltAndroidRule(this);

  // Robolectric tests here.
}

Nếu bạn sử dụng phiên bản Trình bổ trợ Android cho Gradle thấp hơn phiên bản 4.2, hãy bật tính năng chuyển đổi các lớp @AndroidEntryPoint trong kiểm thử đơn vị cục bộ bằng cách áp dụng cấu hình sau trong tệp build.gradle của mô-đun:

Groovy

hilt {
    enableTransformForLocalTests = true
}

Kotlin

hilt {
    enableTransformForLocalTests = true
}

Thông tin thêm về enableTransformForLocalTests trong tài liệu về Hilt.

Các tính năng thử nghiệm

Khi Hilt đã sẵn sàng để sử dụng trong quá trình kiểm thử, bạn có thể dùng một số tính năng để tuỳ chỉnh quy trình kiểm thử.

Chèn các loại trong kiểm thử

Để chèn các loại vào một thử nghiệm, hãy sử dụng @Inject để chèn trường. Để yêu cầu Hilt điền vào các trường @Inject, hãy gọi hiltRule.inject().

Hãy xem ví dụ sau về kiểm thử đo lường:

Kotlin

@HiltAndroidTest
class SettingsActivityTest {

  @get:Rule
  var hiltRule = HiltAndroidRule(this)

  @Inject
  lateinit var analyticsAdapter: AnalyticsAdapter

  @Before
  fun init() {
    hiltRule.inject()
  }

  @Test
  fun `happy path`() {
    // Can already use analyticsAdapter here.
  }
}

Java

@HiltAndroidTest
public final class SettingsActivityTest {

  @Rule public HiltAndroidRule hiltRule = new HiltAndroidRule(this);

  @Inject AnalyticsAdapter analyticsAdapter;

  @Before
  public void init() {
    hiltRule.inject();
  }

  @Test
  public void happyPath() {
    // Can already use analyticsAdapter here.
  }
}

Thay thế một liên kết

Nếu cần chèn một bản sao giả mạo hoặc mô phỏng của một phần phụ thuộc, bạn cần yêu cầu Hilt không sử dụng liên kết mà nó đã sử dụng trong mã phát hành chính thức, đồng thời sử dụng một liên kết khác. Để thay thế một liên kết, bạn cần thay thế mô-đun chứa liên kết bằng một mô-đun kiểm thử có chứa các liên kết mà bạn muốn sử dụng trong quá trình kiểm thử.

Ví dụ, giả sử mã phát hành chính thức khai báo một liên kết cho AnalyticsService như sau:

Kotlin

@Module
@InstallIn(SingletonComponent::class)
abstract class AnalyticsModule {

  @Singleton
  @Binds
  abstract fun bindAnalyticsService(
    analyticsServiceImpl: AnalyticsServiceImpl
  ): AnalyticsService
}

Java

@Module
@InstallIn(SingletonComponent.class)
public abstract class AnalyticsModule {

  @Singleton
  @Binds
  public abstract AnalyticsService bindAnalyticsService(
    AnalyticsServiceImpl analyticsServiceImpl
  );
}

Để thay thế liên kết AnalyticsService trong các kiểm thử, hãy tạo một mô-đun Hilt mới trong thư mục test hoặc androidTest bằng phần phụ thuộc giả mạo rồi chú thích bằng @TestInstallIn. Thay vào đó, tất cả kiểm thử trong thư mục đó sẽ được chèn phần phụ thuộc giả mạo.

Kotlin

@Module
@TestInstallIn(
    components = [SingletonComponent::class],
    replaces = [AnalyticsModule::class]
)
abstract class FakeAnalyticsModule {

  @Singleton
  @Binds
  abstract fun bindAnalyticsService(
    fakeAnalyticsService: FakeAnalyticsService
  ): AnalyticsService
}

Java

@Module
@TestInstallIn(
    components = SingletonComponent.class,
    replaces = AnalyticsModule.class
)
public abstract class FakeAnalyticsModule {

  @Singleton
  @Binds
  public abstract AnalyticsService bindAnalyticsService(
    FakeAnalyticsService fakeAnalyticsService
  );
}

Thay thế liên kết trong một kiểm thử duy nhất

Để thay thế một liên kết trong một kiểm thử đơn thay vì mọi kiểm thử, hãy gỡ cài đặt mô-đun Hilt của một kiểm thử bằng cách sử dụng chú thích @UninstallModules, đồng thời tạo một mô-đun kiểm thử mới bên trong kiểm thử đó.

Theo ví dụ về AnalyticsService từ phiên bản trước, hãy bắt đầu bằng cách yêu cầu Hilt bỏ qua mô-đun phát hành chính thức bằng cách sử dụng chú thích @UninstallModules trong lớp kiểm thử:

Kotlin

@UninstallModules(AnalyticsModule::class)
@HiltAndroidTest
class SettingsActivityTest { ... }

Java

@UninstallModules(AnalyticsModule.class)
@HiltAndroidTest
public final class SettingsActivityTest { ... }

Tiếp theo, bạn phải thay thế liên kết. Tạo một mô-đun mới trong lớp kiểm thử xác định liên kết kiểm thử:

Kotlin

@UninstallModules(AnalyticsModule::class)
@HiltAndroidTest
class SettingsActivityTest {

  @Module
  @InstallIn(SingletonComponent::class)
  abstract class TestModule {

    @Singleton
    @Binds
    abstract fun bindAnalyticsService(
      fakeAnalyticsService: FakeAnalyticsService
    ): AnalyticsService
  }

  ...
}

Java

@UninstallModules(AnalyticsModule.class)
@HiltAndroidTest
public final class SettingsActivityTest {

  @Module
  @InstallIn(SingletonComponent.class)
  public abstract class TestModule {

    @Singleton
    @Binds
    public abstract AnalyticsService bindAnalyticsService(
      FakeAnalyticsService fakeAnalyticsService
    );
  }
  ...
}

Thao tác này chỉ thay thế liên kết cho một lớp kiểm thử duy nhất. Nếu bạn muốn thay thế liên kết cho tất cả các lớp kiểm thử, hãy sử dụng chú thích @TestInstallIn của phần trên. Ngoài ra, bạn có thể đặt liên kết kiểm thử trong mô-đun test dành cho kiểm thử Robolectric, hoặc trong mô-đun androidTest đối với kiểm thử đo lường. Bạn nên sử dụng @TestInstallIn bất cứ khi nào có thể.

Liên kết các giá trị mới

Sử dụng chú thích @BindValue để dễ dàng liên kết các trường trong kiểm thử vào biểu đồ phần phụ thuộc Hilt. Chú thích một trường bằng @BindValue và trường này sẽ được liên kết với loại trường đã khai báo bằng bất kỳ bộ hạn định nào có trong trường đó.

Trong ví dụ AnalyticsService, bạn có thể thay thế AnalyticsService bằng một nội dung giả bằng cách sử dụng @BindValue:

Kotlin

@UninstallModules(AnalyticsModule::class)
@HiltAndroidTest
class SettingsActivityTest {

  @BindValue @JvmField
  val analyticsService: AnalyticsService = FakeAnalyticsService()

  ...
}

Java

@UninstallModules(AnalyticsModule.class)
@HiltAndroidTest
class SettingsActivityTest {

  @BindValue AnalyticsService analyticsService = FakeAnalyticsService();

  ...
}

Việc này giúp đơn giản hoá cả việc thay thế lẫn tham chiếu một liên kết trong quá trình kiểm thử bằng cách cho phép bạn thực hiện cả hai cùng lúc.

@BindValue hoạt động với bộ hạn định và các chú thích kiểm thử khác. Chẳng hạn nếu sử dụng các thư viện kiểm thử như Mockito, bạn có thể sử dụng các thư viện đó trong kiểm thử Robolectric như sau:

Kotlin

...
class SettingsActivityTest {
  ...

  @BindValue @ExampleQualifier @Mock
  lateinit var qualifiedVariable: ExampleCustomType

  // Robolectric tests here
}

Java

...
class SettingsActivityTest {
  ...
  @BindValue @ExampleQualifier @Mock ExampleCustomType qualifiedVariable;

  // Robolectric tests here
}

Nếu cần thêm nhiều liên kết, bạn có thể sử dụng chú thích @BindValueIntoSet@BindValueIntoMap thay cho @BindValue. @BindValueIntoMap cũng yêu cầu bạn chú thích trường bằng chú thích khóa bản đồ.

Các trường hợp đặc biệt

Hilt cũng cung cấp các tính năng để hỗ trợ các trường hợp sử dụng không theo chuẩn.

Tùy chỉnh ứng dụng để thử nghiệm

Nếu bạn không thể dùng HiltTestApplication vì ứng dụng kiểm thử cần mở rộng một ứng dụng khác, hãy chú thích một lớp hoặc giao diện mới bằng @CustomTestApplication, truyền giá trị của lớp cơ sở mà bạn muốn ứng dụng Hilt đã tạo sẽ mở rộng.

@CustomTestApplication sẽ tạo một lớp Application sẵn sàng để thử nghiệm với Hilt, lớp này sẽ mở rộng ứng dụng mà bạn đã truyền dưới dạng tham số.

Kotlin

@CustomTestApplication(BaseApplication::class)
interface HiltTestApplication

Java

@CustomTestApplication(BaseApplication.class)
interface HiltTestApplication { }

Trong ví dụ này, Hilt sẽ tạo một Application có tên là HiltTestApplication_Application mở rộng lớp BaseApplication. Nói chung, tên của ứng dụng đã tạo là tên của lớp chú thích được thêm vào _Application. Bạn phải thiết lập ứng dụng kiểm thử Hilt đã tạo để chạy trongkiểm thử đo lường hoặc Kiểm thử Robolectric như được mô tả trong phần Kiểm thử ứng dụng.

Nhiều đối tượng TestRule trong kiểm thử đo lường

Nếu bạn có các đối tượng TestRule khác trong kiểm thử, có nhiều cách để đảm bảo tất cả quy tắc đều hoạt động cùng nhau.

Bạn có thể gói các quy tắc lại với nhau như sau:

Kotlin

@HiltAndroidTest
class SettingsActivityTest {

  @get:Rule
  var rule = RuleChain.outerRule(HiltAndroidRule(this)).
        around(SettingsActivityTestRule(...))

  // UI tests here.
}

Java

@HiltAndroidTest
public final class SettingsActivityTest {

  @Rule public RuleChain rule = RuleChain.outerRule(new HiltAndroidRule(this))
        .around(new SettingsActivityTestRule(...));

  // UI tests here.
}

Ngoài ra, bạn có thể sử dụng cả hai quy tắc ở cùng một cấp, miễn là HiltAndroidRule được thực thi trước. Hãy chỉ định thứ tự thực thi bằng cách sử dụng thuộc tính order trong chú thích @Rule. Thuộc tính này chỉ hoạt động trong JUnit phiên bản 4.13 trở lên:

Kotlin

@HiltAndroidTest
class SettingsActivityTest {

  @get:Rule(order = 0)
  var hiltRule = HiltAndroidRule(this)

  @get:Rule(order = 1)
  var settingsActivityTestRule = SettingsActivityTestRule(...)

  // UI tests here.
}

Java

@HiltAndroidTest
public final class SettingsActivityTest {

  @Rule(order = 0)
  public HiltAndroidRule hiltRule = new HiltAndroidRule(this);

  @Rule(order = 1)
  public SettingsActivityTestRule settingsActivityTestRule = new SettingsActivityTestRule(...);

  // UI tests here.
}

launchFragmentInContainer

Bạn không thể sử dụng launchFragmentInContainer trong thư viện androidx.fragment:fragment-testing bằng Hilt, vì thư viện này dựa vào hoạt động không được chú thích bằng @AndroidEntryPoint.

Thay vào đó, hãy sử dụng mã launchFragmentInHiltContainer trong kho lưu trữ GitHub của architecture-samples.

Sử dụng một điểm truy cập trước khi có thể dùng thành phần singleton

Chú thích @EarlyEntryPoint cung cấp một giải pháp khi cần tạo một điểm truy cập Hilt trước khi thành phần singleton có trong kiểm thử Hilt.

Vui lòng tìm hiểu thêm thông tin về @EarlyEntryPoint trong tài liệu về Hilt.