robolectric / robolectric

Android Unit Testing Framework
http://robolectric.org
Other
5.9k stars 1.37k forks source link

AppCompatDialogFragment broken with FragmentScenario #5423

Closed Kisty closed 3 months ago

Kisty commented 4 years ago

Description

When launching an AppCompatDialogFragment, it fails. I think it's due to the EmptyActivity the FragmentScenario uses doesn't have R.style.Theme_AppCompat as the style.

java.lang.IllegalStateException: You need to use a Theme.AppCompat theme (or descendant) with this activity.

    at androidx.appcompat.app.AppCompatDelegateImpl.createSubDecor(AppCompatDelegateImpl.java:696)
    at androidx.appcompat.app.AppCompatDelegateImpl.ensureSubDecor(AppCompatDelegateImpl.java:659)
    at androidx.appcompat.app.AppCompatDelegateImpl.setContentView(AppCompatDelegateImpl.java:543)
    at androidx.appcompat.app.AppCompatDialog.setContentView(AppCompatDialog.java:100)
    at androidx.fragment.app.DialogFragment.onActivityCreated(DialogFragment.java:463)
    at androidx.fragment.app.Fragment.performActivityCreated(Fragment.java:2619)
    at androidx.fragment.app.FragmentManagerImpl.moveToState(FragmentManagerImpl.java:904)
    at androidx.fragment.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManagerImpl.java:1238)
    at androidx.fragment.app.FragmentManagerImpl.moveToState(FragmentManagerImpl.java:1303)
    at androidx.fragment.app.BackStackRecord.executeOps(BackStackRecord.java:439)
    at androidx.fragment.app.FragmentManagerImpl.executeOps(FragmentManagerImpl.java:2079)
    at androidx.fragment.app.FragmentManagerImpl.executeOpsTogether(FragmentManagerImpl.java:1869)
    at androidx.fragment.app.FragmentManagerImpl.removeRedundantOperationsAndExecute(FragmentManagerImpl.java:1824)
    at androidx.fragment.app.FragmentManagerImpl.execSingleAction(FragmentManagerImpl.java:1696)
    at androidx.fragment.app.BackStackRecord.commitNow(BackStackRecord.java:293)
    at androidx.fragment.app.testing.FragmentScenario$1.perform(FragmentScenario.java:312)
    at androidx.fragment.app.testing.FragmentScenario$1.perform(FragmentScenario.java:291)
    at androidx.test.core.app.ActivityScenario.lambda$onActivity$2$ActivityScenario(ActivityScenario.java:551)
    at androidx.test.core.app.ActivityScenario$$Lambda$4.run(Unknown Source)
    at androidx.test.core.app.ActivityScenario.onActivity(ActivityScenario.java:561)
    at androidx.fragment.app.testing.FragmentScenario.internalLaunch(FragmentScenario.java:290)
    at androidx.fragment.app.testing.FragmentScenario.launch(FragmentScenario.java:203)
    at uk.co.imagitech.robolectricdialogfragmentissue.MyDialogFragmentTest.dialogFragment_hasOkButton(MyDialogFragmentTest.kt:41)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.robolectric.RobolectricTestRunner$HelperTestRunner$1.evaluate(RobolectricTestRunner.java:546)
    at org.robolectric.internal.SandboxTestRunner$2.lambda$evaluate$0(SandboxTestRunner.java:252)
    at org.robolectric.internal.bytecode.Sandbox.lambda$runOnMainThread$0(Sandbox.java:89)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:748)

Steps to Reproduce

  1. git clone https://github.com/Kisty/RobolectricAppCompatDialogFragmentIssue
  2. ./gradlew a:test

Robolectric & Android Version

Robolectric: 4.3 Android: API 28

Link to a public git repo demonstrating the problem:

https://github.com/Kisty/RobolectricAppCompatDialogFragmentIssue

LitterSun commented 3 years ago
/**
 * Launches a Fragment with given arguments hosted by an empty [FragmentActivity] using
 * given [FragmentFactory] and waits for it to reach a resumed state.
 *
 * This method cannot be called from the main thread.
 *
 * @param fragmentArgs a bundle to passed into fragment
 * @param themeResId a style resource id to be set to the host activity's theme
 * @param factory a fragment factory to use or null to use default factory
 */
inline fun <reified F : Fragment> launchFragment(
    fragmentArgs: Bundle? = null,
    @StyleRes themeResId: Int = R.style.FragmentScenarioEmptyFragmentActivityTheme,
    factory: FragmentFactory? = null
) = FragmentScenario.launch(F::class.java, fragmentArgs, themeResId, factory)

So you should set to the host activity's theme, for example

@Test
    fun dialogFragment_hasOkButton() {
        launchFragment<MyDialogFragment>(null, R.style.AppTheme).apply {
            onFragment { fragment ->
                assertThat(fragment.dialog).isNotNull()
                assertThat(fragment.requireDialog().isShowing).isTrue()
            }
        }
        onView(withText("OK")).inRoot(isDialog()).check(matches(isCompletelyDisplayed()))
    }
MGaetan89 commented 3 months ago

Closing this issue, as the solution was given above.