Issue
I've written a fragment class that, at a certain point, double checks isResumed()
before executing something. I'd like to write tests ensuring that this code runs. However, in my test cases derived from ActivityUnitTestCase
, isResumed()
always seems to return false
. Is there some way to make it return true
in such a test? I'm using the Support Library's fragment classes.
Here's an example test which uses similar code to what my real tests use, and demonstrates the problem. testIsResumed()
always fails because isResumed()
is false, despite having called onStart()
and onResume()
on both the activity and the fragment, and liberal usage of waitForIdleSync()
:
public class FragmentIsResumedTest
extends ActivityUnitTestCase<FragmentIsResumedTest.TestActivity> {
public static class TestFragment
extends android.support.v4.app.Fragment {
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return new LinearLayout(getContext());
}
}
public static class TestActivity
extends android.support.v4.app.FragmentActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new LinearLayout(this));
}
}
public FragmentIsResumedTest() {
super(TestActivity.class);
}
public void testIsResumed() {
startActivity(
new Intent(
getInstrumentation().getTargetContext(),
TestActivity.class),
null, null);
getInstrumentation().waitForIdleSync();
TestActivity activity = getActivity();
TestFragment fragment = new TestFragment();
fragment.onCreate(null);
getInstrumentation().callActivityOnStart(activity);
activity.getSupportFragmentManager().beginTransaction()
.add(android.R.id.content, fragment, "FragmentTag")
.commit();
getInstrumentation().callActivityOnResume(activity);
fragment.onStart();
fragment.onResume();
getInstrumentation().waitForIdleSync();
activity.getSupportFragmentManager().executePendingTransactions();
getInstrumentation().waitForIdleSync();
assertTrue(fragment.isResumed());
}
}
How can I make isResumed()
true for my fragment in unit tests that extend from ActivityUnitTestCase
?
Solution
What's missing is that the test setup in the question never calls Activity.onPostResume()
. In the Support Library's FragmentActivity
, this is where the fragments are resumed from, by calling dispatchResume()
on the FragmentManager
. Simply calling Fragment.onResume()
manually won't cause isResumed()
to return true. And for some reason, Instrumentation.callActivityOnResume()
never calls onPostResume()
, and there's no corresponding callActivityOnPostResume()
.
A simple fix is to make onPostResume()
public in TestActivity
:
public static class TestActivity
extends android.support.v4.app.FragmentActivity {
@Override
public void onPostResume() {
super.onPostResume();
}
/* ... */
}
And call it sometime after callActivityOnResume()
in the test setup. This appears to take care of all pending fragment transactions, so no more need for executePendingTransactions()
. And the following test code works for me even after removing all calls to waitForIdleSync()
. Using Mockito.spy()
, we can see that all the lifecycle methods are called on the fragment:
TestActivity activity = getActivity();
TestFragment fragment = spy(new TestFragment());
getInstrumentation().callActivityOnStart(activity);
activity.getSupportFragmentManager().beginTransaction()
.add(android.R.id.content, fragment, "FragmentTag")
.commit();
getInstrumentation().callActivityOnResume(activity);
activity.onPostResume();
assertTrue(fragment.isResumed());
InOrder inOrder = inOrder(fragment);
inOrder.verify(fragment).onCreate(any(Bundle.class));
inOrder.verify(fragment).onViewCreated(any(View.class), any(Bundle.class));
inOrder.verify(fragment).onActivityCreated(any(Bundle.class));
inOrder.verify(fragment).onStart();
inOrder.verify(fragment).onResume();
Answered By - Dan Getz
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.