Hilt Fragments must be attached to an @AndroidEntryPoint Activity. Found: class androidx.fragment.app.testing.FragmentScenario$EmptyFragmentActivity
Asked Answered
M

5

24

I got following error when i run my unit test code.

Caused by: java.lang.IllegalStateException: Hilt Fragments must be attached to an @AndroidEntryPoint Activity. Found: class androidx.fragment.app.testing.FragmentScenario$EmptyFragmentActivity
at dagger.hilt.internal.Preconditions.checkState(Preconditions.java:83)
at dagger.hilt.android.internal.managers.FragmentComponentManager.createComponent(FragmentComponentManager.java:75)
at dagger.hilt.android.internal.managers.FragmentComponentManager.generatedComponent(FragmentComponentManager.java:63)
at com.zhixin.wedeep.homepage.ui.Hilt_HomePage.generatedComponent(Hilt_HomePage.java:70)
at com.zhixin.wedeep.homepage.ui.Hilt_HomePage.inject(Hilt_HomePage.java:89)
at com.zhixin.wedeep.homepage.ui.Hilt_HomePage.initializeComponentContext(Hilt_HomePage.java:53)
at com.zhixin.wedeep.homepage.ui.Hilt_HomePage.onAttach(Hilt_HomePage.java:45)
at androidx.fragment.app.Fragment.onAttach(Fragment.java:1602)
at com.zhixin.wedeep.homepage.ui.Hilt_HomePage.onAttach(Hilt_HomePage.java:35)
at com.zhixin.wedeep.homepage.ui.HomePage.onAttach(HomePage.kt:281)

This is my test code.

@HiltAndroidTest
@UninstallModules(HomePageDataModule::class)
@RunWith(AndroidJUnit4::class)
@LargeTest
class TestHomePageFragment {

    private val c = Composition("cyrus", "background", "description", "downloadUrl", "1000", "url", "1", true, "100", 100, "100", "test", "title", "1", "100", "cover", ArrayList(), "ONCE", null)

    @Inject
    lateinit var cpd: CompositionDao

    @get:Rule
    var hiltRule = HiltAndroidRule(this)




    @Before
    fun init() {
        hiltRule.inject()
        Util.RETROFIT

        Util.enqueueResponse("mainpage.json")

        cpd.createComposition(c)
        cpd.createBrowseRecord(BrowseRecord(c.id, System.currentTimeMillis()))
        val s = launchFragment<HomePage>()
        s.onFragment {
            IdlingRegistry.getInstance().register(it.mIdleResource)
        }
    /*    dataBindingIdlingResourceRul = DataBindingIdlingResourceRule(s)
        dataBindingIdlingResourceRul.starting(null)*/

    }


    @Test
    fun testDataInitial() {

        onView(ViewMatchers.withId(R.id.recycler_view_preview_data))
                .perform(RecyclerViewActions.scrollToPosition<RecyclerView.ViewHolder>(1))
    }

    @After
    fun finish(){

    }

}

Any idea for this question?

Mayst answered 31/7, 2020 at 10:12 Comment(0)
F
18

As stated on Hilt guide, "Testing" section:

Warning: Hilt does not currently support FragmentScenario because there is no way to specify an activity class, and Hilt requires a Hilt fragment to be contained in a Hilt activity. One workaround for this is to launch a Hilt activity and then attach your fragment.

The error is happening since hilt-managed fragment should be attached to a hilt-managed Activity as well, in short, both should be annotated with @AndroidEntryPoint. Because FragmentScenario uses an EmptyFragmentActivity to hold the underlying fragment being tested, currently there's no way to integrate Hilt with FragmentScenario. An workaround is to launch an activity and then attach the fragment to it.

Felicific answered 4/8, 2020 at 3:41 Comment(1)
I got it, Which means i must creat a test activity for this fragment, becuase that fragment orginal container is a little complicate.Mayst
A
9

For your testing purpose if you want to test fragment instrumented test you can do the following step:

  1. Create New Activity for Activity Container in your debug source set (or if you don't have debug folder/source set just make it in your main package) you can see the code here and register the activity in Android manifest.

  2. And finally create this inline function in your androidTest package.

You can just literally copy-paste code that I have mentioned. It worked well for me. Those code are provided by Manuel Vivo.

Audra answered 31/10, 2020 at 9:31 Comment(2)
Thanks, this answer helped me, but also in combination with github.com/google/dagger/issues/2210#issuecomment-743103051 The thing is, that EmptyFragmentActivity is internal in the latest libraries I guess, so one trick is to use the full reference to that class as a String in the intent.Xebec
How do you do scenario.onFragment {fragment -> setViewNavController } with this approach?Simmie
A
6

The solution lies in this Github project -> https://github.com/android/architecture-samples/blob/dev-hilt/app/src/androidTest/java/com/example/android/architecture/blueprints/todoapp/HiltExt.kt

Autocorrelation answered 21/3, 2021 at 22:29 Comment(1)
Thank you! It provides fast solution. Additionally, you should copy 'debug' folder where Android Folder and HiiltTestActivity are locatedGoldston
B
1

We can do in two ways. Both work with no issues.

  1. I agree on first accepted answer. He, there, didn't provide example. So, I am providing that here.

    import androidx.test.core.app.launchActivity
    import androidx.core.util.Preconditions
    import androidx.fragment.app.Fragment
    import androidx.test.core.app.ActivityScenario
    import androidx.test.ext.junit.rules.ActivityScenarioRule
    import com.example.mvvm.uilayer.ui.activities.YourActivity
    import com.example.mvvm.uilayer.ui.fragments.YourFragment
    import dagger.hilt.android.testing.HiltAndroidRule
    import dagger.hilt.android.testing.HiltAndroidTest
    import org.junit.Before
    import org.junit.Rule
    import org.junit.Test
    
     @HiltAndroidTest
     class YourFragmentTest {
    
         // Enable Hilt for the test class using HiltAndroidRule
         @get:Rule
         var hiltRule = HiltAndroidRule(this)
    
         // Use ActivityScenarioRule to launch YourActivity for testing
         @get:Rule
         var activityRule: ActivityScenarioRule<YourActivity> =
             ActivityScenarioRule(YourActivity::class.java)
    
         @Before
         fun setup() {
             hiltRule.inject()
         }
    
         @Test
         fun testLaunchFragmentInHiltContainer() {
             val activityScenario: ActivityScenario<YourActivity> = launchActivity()
             activityScenario.onActivity {
                     val yourFragment: Fragment = it.supportFragmentManager.fragmentFactory.instantiate(
                         Preconditions.checkNotNull(YourFragment::class.java.classLoader),
                         YourFragment::class.java.name
                     )
    
                 it.supportFragmentManager.beginTransaction()
                     .add(android.R.id.content, yourFragment, "")
                     .commitNow()
    
             }
         }
    
     }
    
  2. We have other way too. Since EmptyAndroidActivity is now internal, which has no @AndroidEntryPoint annotation, the FragmentScenario, which demands @AndroidEntryPoint, won't work. Thus we can create an empty activity, and write one utility class to launch our fragment using that our created empty activity. This is explained in here: https://youtu.be/k4zG93ogWFY, https://github.com/philipplackner/ShoppingListTestingYT/tree/TestingFragmentsWithHilt

Batwing answered 24/7, 2023 at 17:36 Comment(0)
C
0

EmptyFragmentActivity now is internal so, instead of FragmentScenario.EmptyFragmentActivity.THEME_EXTRAS_BUNDLE_KEY use

"androidx.fragment.app.testing.FragmentScenario.EmptyFragmentActivity.THEME_EXTRAS_BUNDLE_KEY"

Full code :

putExtra(
        "androidx.fragment.app.testing.FragmentScenario.EmptyFragmentActivity.THEME_EXTRAS_BUNDLE_KEY",
        themeResId
    )
Cerebrovascular answered 2/9, 2022 at 4:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.