Open LeonRa opened 2 years ago
I did find #2301 and #2370 during my search for solutions, but I'd like to know if something has changed since then
Hi @LeonRa, the situation is still the same as in https://github.com/google/dagger/issues/2301#issuecomment-765729050.
As a workaround, could you move your navigateToSomeScreen
logic into an @Inject
class that you can mock instead? Something like:
class SomeRouter
@AssistedInject
constructor(
@Assisted @IdRes private val fragmentContainerId: Int,
private val navigator: Navigator
) {
fun navigateToSomeScreen() = navigator.navigateTo(fragmentContainerId)
// ...
}
// Replace this class with a mock instead of the factory
class Navigator @Inject constructor(...) {
fun navigateTo(val fragmentContainerId: Int) = /* ... */ ;
}
Thank you for the speedy response @bcorso! This isn't ideal, but makes sense.
What would be the approach when the above delegation approach isn't so clear cut, let's say like an assisted VM?
class SomeViewModel
@AssistedInject
constructor(
@Assisted private val param: SomeRuntimeParam,
private val someDependency: SomeDependency,
) : ViewModel() {
@AssistedFactory
interface Factory {
fun create(param: SomeRuntimeParam): SomeViewModel
}
}
The testing philosophy of Hilt is to try to use real dependency as much as possible (this may mean testing the overall result of an action, e.g. in the case of the Router
you might test that the activity is displaying the expected fragment rather than trying to intercept the navigateToSomeScreen
call).
In the case of SomeViewModel
, it really depends what you're trying to mock out. Do you really need to mock out the entire view model?
If you really need to mock your assisted factory then you could probably use the approach in https://github.com/google/dagger/issues/2301#issuecomment-765729050 combined with @TestInstallIn
to provide a fake for your factory.
The philosophy makes total sense, thank you for the link. I'll be sure to give my approach more thought.
In case you're curious, here is some context around my current experimentation: We already unit test ViewModels, Repositories and other classes in the model layer, so the thinking is to try validating the UI layer by providing it with completely mocked state. While this won't necessarily replace longer instrumentation / E2E tests, I'm interested in seeing if it can provide enough coverage to avoid the writing of more brittle multi-screen flows, setup of network fakes, and/or the addition of things like idling resources to production code.
You've been a great help so far and I'd love to hear your thoughts here. Absolutely no pressure though, as I realize I'm asking for more of your time.
Unless I'm doing something wrong, it seems that
@HiltAndroidTest
doesn't play well with@AssistedFactory
.In my use-case, I would like to replace the injected factory with one that I can configure to return a mock during instrumented tests. My setup uses MVVM, though it uses a command pattern for pushing routing emissions (and other single-fire events) to its View.
I have a Router that is capable of performing some Activity/Fragment navigation but requires an assisted parameter:
I have a ViewModel which emits routing commands to any subscribers using an RxJava Flowable:
Both of these are then used by an Activity, which ties the ViewModel emissions to a Router created by using a property-injected Factory:
The use case here is being able to write an instrumentation test which validates that when the ViewModel emits a route, the Activity propagates it to the Router. Using Mockk, this is what I have at the moment:
As is, this code fails with
Dagger does not support providing @AssistedFactory types
due to the@BindValue
of theRouter
factory.I did find #2301 and #2370 during my search for solutions, but I'd like to know if something has changed since then, given the strong push for Hilt over the last year. While my use-case may not be common, I can see others running into similar use-cases with classes like ViewModels requiring runtime parameters.
Thank you in advance!