question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Hilt: test Android components in isolation

See original GitHub issue

Use case

I have this Gradle modules configuration:

            app
             |
             A
           /   \
          B     C
        /  \    / \
       D    E  F   G

Each one of these Gradle modules is an implementation project('...') dependency.


Each one of these Gradle modules has a Hilt module:

@Module
@InstallIn(ApplicationComponent::class)
internal object Module[X] { ... }
// for X in (A, B, C, D, E)

Some of these Dagger modules depend on values provided by some other Gradle modules included by app, or by app itself.


I have a MainActivity in the Gradle module A with some dependencies.

@AndroidEntryPoint
class MainActivity: AppCompatActivity() {
  @Inject lateinit var dep1: Dep1
  @Inject lateinit var dep2: Dep2
}

I want to test my MainActivity in isolation providing Dep1 and Dep2 manually in the test. Something like this:

@HiltAndroidTest
@UninstallModules(...)
class MainActivityTest {
  @get:Rule val hiltRule = HiltAndroidRule(this)
  @BindValue @JvmStatic val dep1 = mock<Dep1>()
  @BindValue @JvmStatic val dep2 = mock<Dep2>()
  @Before fun setup() { hiltRule.inject() }
}

As far as I understand, the only way I can do this is by uninstalling all dagger modules of the generated graph of Gradle module A. Without uninstalling all modules, dagger will not be able to resolve the dependencies provided by app unless I provide them in my test with @BindValue (which would be an overkill).

I should do something like this:

@UninstallModules(
  ModuleA::class, 
  ModuleB::class, 
  ModuleC::class, 
  ModuleD::class, 
  ModuleE::class, 
  ModuleF::class, 
  ModuleG::class
)

Is that right?

We have a pretty large project and this is a very common use case.

Sometimes we don’t have access to the dagger modules we want to uninstall, they are in the classpath but they are not visible from the source code for several reasons:

  1. Dagger modules can be internal
  2. Some modules are an implementation dependency of another implementation dependency (see D, E, F and G)

We could make all modules public and all Gradle dependencies api but that wouldn’t be a clean solution.

For large projects manually checking all indirect dagger modules to uninstall them would be really painful.


Is there a non-painful way to test Android components in isolation for this use case?


Does it make sense a @UninstallAllModules annotation that automatically clears the entire dagger graph? This looked the only way to solve this issue to me, but since I still have a little experience with Hilt, it could sound totally stupid.

This is the way we will probably proceed in our private project to solve this issue, but an “official” API or guidance would be highly appreciated 😄

We solved this issue with Dagger-Android in a really easy way. This is the only reason that stops us from migrating activities/fragment injection to Hilt.

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Reactions:3
  • Comments:19 (10 by maintainers)

github_iconTop GitHub Comments

1reaction
Chang-Ericcommented, Aug 4, 2020

I think it’d be very difficult to set the expectations correctly on what should or shouldn’t work in this case and it isn’t even clear to me where that line should be drawn, especially with extensions like ViewModels or any other extension library someone might create. I also want @BindValue to be basically a syntactic sugar as opposed to anything different about using a module to keep things simpler for people so I wouldn’t want to special case it.

I think I understand more what you are trying to do and I think I agree this is where it starts to involve a testing philosophy clash. Thanks for realizing that sooner than I did and still continuing to explain it to me =)

1reaction
Chang-Ericcommented, Aug 3, 2020

it’d start to get painful if those things didn’t work for you

As you said, “In Dagger, the default as you mention is modules aren’t installed”, so I’d expect people to be used to write tests in this kind of environment.

Just to clarify, I was referring to new things like @BindValue where people aren’t already using it with normal Dagger. In this case, if you did @BindValue Foo foo; and you used this option to flip the default, you’d also somehow need to reference the generated module somewhere like @InstallModulesIntoThisTest(BindValue_GeneratedFooModule.class) (class name is an example, I didn’t actually look up what the generated name is). This would probably severely impact the usefulness of such a feature. Same thing would be for other libraries, like you’d probably need to pull in some internal ViewModel Hilt extension modules. So it’d be breaking a lot of encapsulation that Hilt is trying to make easier.

Thanks for understanding my point of view and for this discussion. Even though I closed this and your other pull request, I do really appreciate the contributions and effort.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Hilt testing guide | Android Developers
The @EarlyEntryPoint annotation provides an escape hatch when a Hilt entry point needs to be created before the singleton component is available in...
Read more >
Android — Instrumentation test with hilt | Mahendran
For instrument tests (androidTest), hilt is responsible for instantiating the ViewModel. So, we need someone who speaks Hilt's language. Create ...
Read more >
Testing Fragments with Dagger-Hilt - Part 12 - YouTube
In this video I will show you a workaround to be able to test fragments with the help of Dagger- Hilt.⭐ Get certificates...
Read more >
Hilt makes testing easier on Android - YouTube
The best android courses in the world: https://codingwithmitch.com/Watch the HILT course here (FREE): ...
Read more >
Testing with Hilt android - Nyame Bismark - Medium
As Hilt creates new components for tests that use @UninstallModules , it can significantly impact unit test build times. Use it when necessary...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found