要在配置更改中保留我的AsyncTasks,我使用基于片段的解决方案和setRetainInstance(true),该解决方案托管每个AsyncTask并回调一个监听活动,类似于此解决方案http://www.androiddesignpatterns.com/2013/04/retaining-objects-across-config-changes.html
最终,目的是使用Robolectric在整个配置更改中测试AsyncTask的保留功能,但我需要从正确设置实际配置更改开始。但是,似乎我无法模仿配置更改期间发生的确切参考行为。
真实应用:在运行真实应用时,在配置更改时,会在保留片段时销毁并重新创建活动,因此它似乎正在运行。我可以通过在配置更改之前和之后检查它们的引用来看到这一点(下面使用的示例引用):
真实应用,之前: 活动: abc 片段: xyz
真实应用,之后: 活动: bca 片段: xyz(正确保留并重新连接)
案例1:然而,当在Robolectric测试中的Activity上运行recreate()时,Activity似乎没有正确地重新创建它的实例(尽管文档说该方法执行所有生命周期调用):
mActivityController =
Robolectric.buildActivity(AsyncTaskTestActivity.class).attach().create().start().resume().visible();
mActivity = mActivityController.get();
mActivity.recreate();
Robolectric with recreate(),之前: 活动: abc 片段: xyz
Robolectric with recreate(),之后 活动: abc 片段: xyz
这让我相信没有正确创建新的Activity实例,因此重新附加功能没有以真实的方式发生。
案例2:如果我根据个别生命周期调用创建测试:
mActivityController = Robolectric.buildActivity(AsyncTaskTestActivity.class).attach().create().start().resume().visible();
mActivityController.pause().stop().destroy();
mActivityController = Robolectric.buildActivity(AsyncTaskTestActivity.class).attach().create().start().resume().visible();
在这个版本中,似乎Activity从头开始完全替换,但Fragment也是如此:
Robolectric之前有单独的生命周期调用 活动: abc 片段: xyz
Robolectric,具有单独的生命周期调用,之后 活动: bca 片段: yzx
我似乎正在重复使用相同的Activity(案例1)或用新实例替换所有内容,就好像没有底层应用程序保留Fragment(案例2)。
问题:有什么方法可以设置我的Robolectric测试来模仿在实际Android环境中运行应用程序时获得的参考结果(根据Real应用程序结果),或者我是不是要么创建一个单独的测试应用程序或者使用Robotium功能测试?我试着这样做https://stackoverflow.com/a/26468296,但得到的结果与我的情况2相同。
提前致谢!
答案 0 :(得分:2)
我玩了很多,并提出了使用Robolectric 3.0和Mockito的解决方案:
@RunWith(RobolectricGradleTestRunner.class)
@Config(constants = BuildConfig.class, sdk = Build.VERSION_CODES.KITKAT, shadows = {ExampleActivityTest.ExampleActivityShadow.class})
public class ExampleActivityTest {
@Mock
private FragmentManager fragmentManagerMock;
@Before
public void setup() {
initMocks(this);
setupFragmentManagerMock();
}
@Test
public void testRestoreAfterConfigurationChange() {
// prepare
ActivityController<ExampleActivity> controller = Robolectric.buildActivity(ExampleActivity.class);
ExampleActivity activity = controller.get();
ExampleActivityShadow shadow = (ExampleActivityShadow) Shadows.shadowOf(activity);
shadow.setFragmentManager(fragmentManagerMock);
ActivityController<ExampleActivity> controller2 = Robolectric.buildActivity(ExampleActivity.class);
ExampleActivity recreatedActivity = controller2.get();
ExampleActivityShadow recreatedActivityShadow = (ExampleActivityShadow) Shadows.shadowOf(recreatedActivity);
recreatedActivityShadow.setFragmentManager(fragmentManagerMock);
// run & verify
controller.create().start().resume().visible();
activity.findViewById(R.id.inc_button).performClick();
activity.findViewById(R.id.inc_button).performClick();
assertEquals(2, activity.lostCount.count);
assertEquals(2, activity.retainedCount.count);
Bundle bundle = new Bundle();
controller.saveInstanceState(bundle).pause().stop().destroy();
controller2.create(bundle).start().restoreInstanceState(bundle).resume().visible();
assertEquals(0, recreatedActivity.lostCount.count);
assertEquals(2, recreatedActivity.retainedCount.count);
}
private void setupFragmentManagerMock() {
final HashMap<String, Fragment> fragments = new HashMap<>();
doAnswer(new Answer<Object>() {
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
return fragments.get(invocation.getArguments()[0]);
}
}).when(fragmentManagerMock).findFragmentByTag(anyString());
final HashMap<String, Fragment> fragmentsToBeAdded = new HashMap<>();
final FragmentTransaction fragmentTransactionMock = mock(FragmentTransaction.class);
doAnswer(new Answer<Object>() {
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
fragmentsToBeAdded.put((String) invocation.getArguments()[1], (Fragment) invocation.getArguments()[0]);
return fragmentTransactionMock;
}
}).when(fragmentTransactionMock).add(any(Fragment.class), anyString());
doAnswer(new Answer<Object>() {
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
fragments.putAll(fragmentsToBeAdded);
return null;
}
}).when(fragmentTransactionMock).commit();
when(fragmentManagerMock.beginTransaction()).thenReturn(fragmentTransactionMock);
}
@Implements(Activity.class)
public static class ExampleActivityShadow extends ShadowActivity {
private FragmentManager fragmentManager;
@Implementation
public FragmentManager getFragmentManager() {
return fragmentManager;
}
public void setFragmentManager(FragmentManager fragmentManager) {
this.fragmentManager = fragmentManager;
}
}
}
请注意,我只是模仿了我在代码中使用的FragmentManager(beginTransaction()
和findFragmentByTag()
)和FragmentTransaction(add()
和commit()
)的方法,所以你可能需要根据您的代码扩展它们。
我还没有对Robolectric做过太多的工作,所以可能会有一个更优雅的解决方案,但这对我现在有用。
您可以在此处查看完整的源代码和项目设置:https://github.com/rgeldmacher/leash(如果您仍需要保留对象,可能值得一看;)