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

时间:2011-05-19 18:53:22

标签: android unit-testing screen-orientation robolectric

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

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

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

感谢。

4 个答案:

答案 0 :(得分:19)

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

如果在Robolectric测试中调用一个Activity上的重新创建,则会发生以下情况:

  1. 的onSaveInstanceState
  2. 的onPause
  3. 的onStop
  4. 的onDestroy
  5. 的onCreate
  6. 在onStart
  7. onRestoreInstanceState
  8. 的onResume
  9. (我通过覆盖测试活动中的大多数生命周期方法并将日志语句放入其中来发现这一点)

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

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

    (此代码适用于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
    11. 这仍然不是完全匹配,但更接近于遇到真正的配置更改时会发生的情况。

      我认为这可能是对由于内存不足而导致活动被破坏时所发生的事情的有效模拟,因为与调用recreate()不同,我不认为这会保留对保留片段的引用。我虽然在这个地区摇摇欲坠!

      更新

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

      Robolectric.buildActivity(YourActivity.class).withIntent(intent).create(bundle) // and so on...
      

答案 1 :(得分: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();

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

答案 2 :(得分:8)

您要针对哪些Android API级别进行编译?如果它是3.0或更高,你可以尝试Activity.recreate()。文档说明:

  

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

我自己没试过。

答案 3 :(得分:3)

Robolectric的ActivityController类有一个可能处理此问题的configurationChange()方法。天啊,它甚至有一个javadoc评论! :d