使用Robolectric进行配置更改

jom*_*mni 5 android android-lifecycle android-fragments robolectric android-testing

为了在配置更改中保留我的AsyncTasks,我使用基于片段的解决方案和setRetainInstance(true),它托管每个AsyncTask并回调一个监听活动,类似于此解决方案http://www.androiddesignpatterns.com/2013/04 /retaining-objects-across-config-changes.html

最终,目的是使用Robolectric在整个配置更改中测试AsyncTask的保留功能,但我需要从正确设置实际配置更改开始.但是,似乎我无法模仿配置更改期间发生的确切参考行为.


真正的应用程序:在运行真正的应用程序时,在配置更改时,在保留Fragment时销毁并重新创建Activity,因此它似乎正在工作.我可以通过在配置更改之前和之后检查它们的引用来看到这一点(下面使用的示例引用):

  • 真正的应用程序,之前: 活动: abc 片段: xyz

  • 真正的应用程序,之后: 活动: bca 片段: xyz(正确保留和重新附加)


情况1:然而,当在Robolectric测试中对Activity运行recreate()时,Activity似乎没有正确地重新创建其实例(尽管文档说该方法执行所有生命周期调用):

mActivityController =
Robolectric.buildActivity(AsyncTaskTestActivity.class).attach().create().start().resume().visible();

mActivity = mActivityController.get();
mActivity.recreate();
Run Code Online (Sandbox Code Playgroud)
  • Robolectric与recreate(),之前: 活动: abc 片段: xyz

  • Robolectric with recreate(),在Activity之后 : abc Fragment: 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();
Run Code Online (Sandbox Code Playgroud)

在这个版本中,似乎Activity从头开始完全替换,但片段也是如此:

  • Robolectric具有单独的生命周期调用,在Activity之前 : abc Fragment: xyz

  • Robolectric有独立的生命周期通话,后 活动: BCA 片段: YZX


看来我要么重用相同的Activity(案例1),要么用新实例替换所有内容,好像没有底层应用程序保留Fragment(案例2).

问题:有没有什么方法可以设置我的Robolectric测试来模仿在实际Android环境中运行应用程序时获得的参考结果(根据Real应用程序结果),或者我是否仍然坚持要么创建一个单独的测试应用程序或者使用Robotium功能测试解决?我尝试这样做/sf/answers/1852780751/,但得到了与我的情况2相同的结果.

提前致谢!

ct_*_*rob 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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,我仅模拟了我在代码中使用的FragmentManager (beginTransaction()findFragmentByTag()) 和 FragmentTransaction (add()和)的方法,因此您可能需要根据您的代码扩展这些方法。commit()

我还没有对 Robolectric 做过太多工作,所以可能有一个更优雅的解决方案,但这目前对我有用。

您可以在此处查看完整的源代码和项目设置: https: //github.com/rgeldmacher/leash(如果您仍然需要保留对象,可能值得一看;))