如何在Android Robolectric测试中强制配置更改?

dav*_*son 20 android unit-testing screen-orientation robolectric

我正在使用robolectric使我的android单元测试足够快,有用.我想测试我编写的代码,同时屏幕方向正在改变以模拟一个常见的现实世界用例.

具体来说,我正在测试的是对服务器的异步http调用,在获取结果后解析了一些xml.我对所有工作得很好的单元测试,但无法弄清楚如何模拟屏幕旋转.导致活动重新创建的任何状态更改都很好,它不一定必须是屏幕旋转.

使用模拟器的解决方案不是一个选项,因为我每分钟运行几次测试,它们必须在2秒内运行.如果可能的话,我也希望这能与roboguice合作.

谢谢.

ZoF*_*reX 19

在Robolectric中调用重新创建(请注意,在使用Robolectric时不必担心旧的API版本)非常接近于模拟配置更改,但不一定会捕获您可能发生的所有错误.特别是它不会创建Activity的新实例(我很确定它不会'擦除'它),所以如果你忘记恢复Activity的成员字段,你的测试将无法捕获它.它确实可以很好地测试片段(非保留片段被破坏并重新实例化).

如果在Robolectric测试中对Activity调用recreate,则会发生以下情况:

  1. 的onSaveInstanceState
  2. 在onPause
  3. 的onStop
  4. 的onDestroy
  5. 的onCreate
  6. 在onStart
  7. onRestoreInstanceState
  8. 的onResume

(我通过覆盖测试活动中的大多数生命周期方法并将日志语句放入其中来发现这一点)

您可以使用以下代码更接近真正的配置更改:

Bundle bundle = new Bundle();
activityController.saveInstanceState(bundle).pause().stop().destroy();
controller = Robolectric.buildActivity(YourActivity.class).create(bundle).start().restoreInstanceState(bundle).resume();
activity = controller.get();
Run Code Online (Sandbox Code Playgroud)

(此代码适用于Robolectric 2.1 - 如果您使用的是2.2或更高版本,那么.visible()之后您可能需要拨打电话.resume())

使用上面的内容,您将看到以下事件发生:

  1. 的onSaveInstanceState
  2. 在onPause
  3. 的onStop
  4. 的onDestroy
  5. 实例化Activity的新实例(以下所有调用都在此新实例上)
  6. 的onCreate
  7. 在onStart
  8. onRestoreInstanceState
  9. 的onResume
  10. onPostResume

这仍然不是完全匹配,但更接近于遇到真正的配置更改时会发生的情况.

我认为这可能是一个很好的模拟,当一个活动因内存不足而被破坏时会发生什么,就像调用recreate()不同,我不认为这会保留对保留片段的引用.我虽然在这个地区摇摇欲坠!

更新:

如果您的Activity是通过intent启动的,则可能需要添加对withIntent的调用,如下所示:

Robolectric.buildActivity(YourActivity.class).withIntent(intent).create(bundle) // and so on...
Run Code Online (Sandbox Code Playgroud)


sud*_*der 10

我已经成功使用了ZoFreX的答案,但是我想添加如何实际模拟旋转.我知道OP指定旋转不是绝对必须的,但是标题暗示这应该包含在答案中,并且可以帮助那些最终在这里偏离的人.

基本上,在应用ZoFrex解决方案之前设置活动的方向.或者更简洁的代码:

// toggle orientation
int currentOrientation = fragment.getActivity().getResources().getConfiguration().orientation;
boolean isPortraitOrUndefined = currentOrientation == Configuration.ORIENTATION_PORTRAIT || currentOrientation == Configuration.ORIENTATION_UNDEFINED;
int toOrientation = isPortraitOrUndefined ? Configuration.ORIENTATION_LANDSCAPE : Configuration.ORIENTATION_PORTRAIT;
Robolectric.application.getResources().getConfiguration().orientation = toOrientation;

// ZoFreX's solution
Bundle bundle = new Bundle();
activityController.saveInstanceState(bundle).pause().stop().destroy();
controller = Robolectric.buildActivity(YourActivity.class).create(bundle).start().restoreInstanceState(bundle).resume();
activity = controller.get();
Run Code Online (Sandbox Code Playgroud)

请查看ZoFreX的解决方案,因为它包含此处未包含的其他信息.


fut*_*sta 9

你正在编译什么Android API级别?如果它是3.0或以上你可以尝试Activity.recreate().文件说明:

导致使用新实例重新创建此活动.这导致与由于配置更改而创建Activity时基本相同的流 - 当前实例将经历其生命周期到onDestroy(),然后在其后创建新实例.

虽然没有尝试过.