I'm trying to make Fragment
testing on Android with Robolectric
and I know there's been some other posts about problems with FragmentActivity
when it hasn't been stored in some variable.
However, I was trying to make this post work and you can see in this case the FragmentActivity
object reference is kept so there shouldn't be a problem with that. Here is the base class:
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import org.junit.After;
import org.robolectric.Robolectric;
import org.robolectric.util.ActivityController;
public class FragmentTestCase<T extends Fragment> {
private static final String FRAGMENT_TAG = "fragment";
private ActivityController controller;
private FragmentActivity activity;
private T fragment;
public void startFragment(T fragment) {
this.fragment = fragment;
controller = Robolectric.buildActivity(FragmentActivity.class);
activity = (FragmentActivity) controller.create().start().visible().get();
activity.getSupportFragmentManager()
.beginTransaction()
.add(fragment, FRAGMENT_TAG).commit();
}
@After
public void destroyFragment() {
if (fragment != null) {
activity.getSupportFragmentManager()
.beginTransaction()
.remove(fragment)
.commit();
fragment = null;
}
}
public void pauseAndResumeFragment() {
controller.pause().resume();
}
public T recreateFragment() {
activity.recreate();
fragment = (T) activity.getSupportFragmentManager()
.findFragmentByTag(FRAGMENT_TAG);
return fragment;
}
}
And here is the implementation of the test:
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricGradleTestRunner;
import org.robolectric.annotation.Config;
import static org.assertj.core.api.Assertions.*;
@RunWith(RobolectricGradleTestRunner.class)
@Config(constants=BuildConfig.class, sdk=21)
public class MainActivityFragmentTest extends FragmentTestCase<MainActivityFragment> {
private MainActivityFragment fragment;
@Before
public void setUp() throws Exception {
fragment = new MainActivityFragment();
}
@Test
public void createsAndDestroysFragment() throws Exception {
startFragment(fragment);
assertThat(fragment).isNotNull();
}
@Test
public void pausesAndResumesFragment() throws Exception {
startFragment(fragment);
pauseAndResumeFragment();
assertThat(fragment).isNotNull();
}
@Test
public void recreatesFragment() throws Exception {
startFragment(fragment);
fragment = recreateFragment();
assertThat(fragment).isNotNull();
}
}
Once I run the test this is the error that I get:
java.lang.IllegalStateException: Activity has been destroyed
at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1399)
at android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:637)
at android.support.v4.app.BackStackRecord.commit(BackStackRecord.java:616)
at com.feyserflabs.musync.FragmentTestCase.destroyFragment(FragmentTestCase.java:36)
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.RunAfters.evaluate(RunAfters.java:33)
at org.robolectric.RobolectricTestRunner$2.evaluate(RobolectricTestRunner.java:245)
at org.robolectric.RobolectricTestRunner.runChild(RobolectricTestRunner.java:185)
at org.robolectric.RobolectricTestRunner.runChild(RobolectricTestRunner.java:54)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.robolectric.RobolectricTestRunner$1.evaluate(RobolectricTestRunner.java:149)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)
It seems that at some point the Activity is destroyed before entering the destroyFragment
method, but I can't find where o why. Any help would be much appreciated.
When you call activity.recreate() you actually destroy your current activity and the reference you keep becomes invalid. You then try to use the FragmentManager of a destroyed activity and it crashes.