Writing An Integration Test With Jetpack Compose and Dagger Hilt

Michel Onwordi
Nerd For Tech
Published in
5 min readApr 16, 2021
Photo by Heri Mulyana on Unsplash

This post will show a simple setup for running an integration test between Compose and a ViewModel using Hilt.

Since Compose turned beta, I’ve been spending time getting familiar with it. I chose a side project as my learning medium — a simple app that displays a list of inventory items. Being a “responsible” engineer, I decided to add tests from the initial stages. Compose and Hilt's teams have done an excellent job documenting them, so information regarding testing is easily found. However, I didn’t find much on a scenario involving both.

Project Structure

Let’s assume the following structure.

The Repository, Viewmodel and Activity are wired together using Hilt to manage their dependencies.

Test Scenario

For this article, we’ll go with a simple integration test scenario, verifying our UI displays a list of inventory items. Ideally, this test helps us ensure that our ViewModel and Compose UI are working together as intended.

(Please remember this is a contrived example. An argument could be made regarding the value of such a test. However, that’s not the focus of this post)

Let’s take a closer look at some of the code in our project to understand better how to achieve this test.

The Code Under Test

Main Activity: Entry point for both Compose and Hilt. The InventoryViewModel here is provided by Hilt.
InventoryViewModel: Responsible for reading data from the repository and emitting a UiState.
Inventory List Item: A Composable responsible for displaying a list of inventory items.

(This article won’t go into details explaining how Hilt works, however, please note the annotations @AndroidEntryPoint , @HiltViewModel , @Inject are all related to the Hilt setup required to use it for dependency injection. More info here)

Looking at the code snippets above, we can observe the InventoryViewModel depends on the InventoryRepository for data. The repository would typically be responsible for fetching this data from the cloud.

When testing, we aim to create a scenario that is as close to production as possible while still being predictable and reliable. Therefore, it’s in our best interest to avoid hitting any actual servers for data. Instead, we’d prefer to provide a hard-coded list of items for test assertions.

Taking a closer look at the InventoryRepository, we see that it’s an interface. This is significant because it gives us the flexibility to provide any concrete implementation we like.

However, we would like the swap to only occur while testing. This is where Hilt and its testing capabilities come into the picture. We’ll see what those are in a few moments.

To accomplish our testing goal, we’ll require the following:

  • A predetermined list of items used for testing
  • Test dependencies for Hilt: Hilt provides API’s that allow us to manipulate the dependency injection process to suit our test needs. We can achieve this with the HiltAndroidRule, HiltTestApplication and HiltTestRunner
  • Test dependencies for Compose: This provides the ComposeTestRule which we’ll use to “load” our composable and perform assertions.
  • Patience 😂

Let’s get to it…

Adding the test dependencies

The following dependencies give us access to the test rules and other components for writing our test.

Providing the fake list of items

Hilt makes it pretty easy to do this. The basic premise is to substitute a real dependency with a test version by replacing the module used to provide that dependency.

Hilt module to provide a fake implementation of list repository while testing

This code basically results in the creation of the module applicable only during testing. Using the @TestInstallIn annotation provided by Hilt’s test artefacts, we can specify:

  • the relevant component scope for the dependency. This is done using the components parameter,
  • the module that should be replaced by this test module using the replaces parameter.

Installing the HiltTestApplication

Using Hilt requires an application class annotated with @HiltAndroidApp . This annotation generates a top-level component used by Hilt to provide dependencies to other components (e.g. The ActivityComponent created by the @androidEntryPoint annotation.)

The same requirement applies during testing. Fortunately, Hilt provides a test application we can use and here’s how the setup looks.

We’ll use this test runner in place of the default test runner by specifying this line in our project-level Gradle file.

NOTE: Use your package name

Finally, write the test.

In this last step, we’re finally ready to write the test class.

Ui test written using Hilt and Compose

There are a few things we should be aware of while writing the test:

  • To run an instrumentation test with Hilt, we’ll annotate the test class with @HiltAndroidTest.
  • The HiltAndroidRule allows us to perform injections within the test by calling inject(). This will enable us to replace the ViewModel’s repository dependency with the fake we defined earlier.
  • The ComposeTestRule is necessary to load the main(root) composable onto the screen. This is done by calling setContent on the test rule and passing in the composable.

Ordering the test rules

To successfully write a functioning UI test using both Compose and Hilt, both test rules must be accessible as class-level properties to allow access to the test API’s mentioned above. Additionally, Hilt requires its test rule to be executed first. JUnit provides two approaches for accomplishing this.

  • RuleChain API: Using this API, we could easily specify the order like this:

However, that would prevent the rules from being accessible as individual properties, thus keeping us from calling inject() and setContent{} on the Hilt and Compose test rules, respectively. Therefore, this (👇🏽) is a better approach.

  • The order parameter: JUnit 4.13 provides an even simpler way to achieve these constraints by using the order parameter of the Rule annotation. All you need to do is provide a numerical value for each rule. The provided numerical values are used to sort the rules in ascending order. Here’s what the code would look like using this approach:

Putting everything together, we’ll end up with this trivial but functional integration test.

Following the steps described above in our simplified scenario, you should be able to write an integration test for your Composed based UI’s with Hilt injected dependencies. The key takeaways are to ensure both test rules are accessible as class-level properties within the test and to use Hilt’s mechanisms for providing fake implementations while testing.

Happy Composing and Testing.

I want to thank Jossi Wolf, Hannes Struß and Lisa Onwordi for their feedback while writing this article.

--

--