2

I have a simple Fragment like so:

class SomeFragment : DaggerFragment() {
    ...
}

Now I want to test this Fragment using FragmentScenario

class LoginFragmentTest {

    @Test
    fun test() {
        launchFragmentInContainer<SomeFragment>()
        onView(withId(R.id.someButton))
            .check(matches(isDisplayed()))
    }

}

But everytime I try to the test its always:

java.lang.IllegalArgumentException: No injector was found for <...SomeFragment>

How can I properly run the test? Can anybody help me here?

Archie G. Quiñones
  • 11,638
  • 18
  • 65
  • 107

1 Answers1

5

I found two ways to solve the problem:

I consider the first approach cleaner and would recommend to use it rather than the latter, but I'll describe both of them, so you can make your own choice.


Build flavors with typealias

  1. Create two product flavors, mock and prod:

app/build.gradle

android {
    ...
    productFlavors {
        prod
        mock {
            applicationIdSuffix '.mock'
        }
    }
}
  1. Then set the typealias depending on the flavor, let's call it BaseFragment:

prod flavor, app/src/prod/com.example.mypackage/BaseFragment.kt

typealias BaseFragment = DaggerFragment

mock flavor, app/src/mock/com.example.mypackage/BaseFragment.kt

typealias BaseFragment = Fragment
  1. And finally use the BaseFragment alias in your fragments:
class SomeFragment : BaseFragment() {
   ...
}
  1. To test the code using FragmentScenario switch from the prod* build variant to the mock* one and set all the dependencies that are supposed to be injected somewhere in your test class (e.g. using mocks)

Own DaggerFragment and test variants of the fragments

  1. Create your own DaggerFragment (based on the actual dagger.android.support.DaggerFragment implementation):
abstract class DaggerFragment() : Fragment(), HasAndroidInjector {

    @Inject
    lateinit var androidInjector: DispatchingAndroidInjector<Object>

    override fun onAttach(context: Context) {
        injectMembers()
        super.onAttach(context)

    override fun androidInjector(): AndroidInjector<Object> = androidInjector

    protected open fun injectMembers() = AndroidSupportInjection.inject(this)
}
  1. Extend your DaggerFragment implementation and set the fragment open:
open class SomeFragment : DaggerFragment() {
    ...
}
  1. Create another implementation of your fragment for the test purposes and override the injectMembers method:
class TestSomeFragment : SomeFragment() {

    override injectMembers() {
        /* you can manually inject dependencies and mocks here */
    }
}
  1. Use TestSomeFragment in your tests.
jsamol
  • 3,042
  • 2
  • 16
  • 27
  • my only issue with this approach is that now, when you run test at other variants, the test would fail. – Archie G. Quiñones Dec 25 '19 at 13:10
  • @ArchieG.Quiñones Yeah, unfortunately this can be a drawback. However, separating test and production variants of code using product flavors is [recommended by Google developers](https://android-developers.googleblog.com/2015/12/leveraging-product-flavors-in-android.html?m=1), so at least it's an officially suggested inconvenience. – jsamol Dec 27 '19 at 21:37
  • Yeah I kinda agree. I just wish that there is a much better solution than this but I think this is enougn thank you very much :) – Archie G. Quiñones Dec 28 '19 at 08:55