20

我正在使用 robolectric 来使我的 android 单元测试足够快以有用。我想在屏幕方向改变以模拟常见的现实世界用例时测试我编写的代码是否有效。

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

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

谢谢。

4

4 回答 4

19

在 Robolectric 中调用 recreate(请注意,使用 Robolectric 时不必担心旧的 API 版本)非常接近于模拟配置更改,但不一定会捕获您可能犯的所有错误。特别是它不会创建 Activity 的新实例(而且我很确定它不会“擦洗”它),因此如果您忘记恢复 Activity 的成员字段,您的测试将无法捕捉到这一点。不过,它确实可以很好地测试片段(未保留的片段被销毁并重新实例化)。

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

  1. onSaveInstanceState
  2. 暂停
  3. 停止
  4. 销毁
  5. 创建
  6. 开始
  7. onRestoreInstanceState
  8. 恢复

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

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

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. 暂停
  3. 停止
  4. 销毁
  5. 实例化 Activity 的新实例(所有后续调用都在这个新实例上)
  6. 创建
  7. 开始
  8. onRestoreInstanceState
  9. 恢复
  10. onPostResume

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

我认为这可能是对由于内存不足而破坏活动时发生的情况的一个不错的模拟,因为与调用 recreate() 不同,我认为这不会保留对保留片段的引用。不过,我在这个地区的处境很不稳定!

更新:

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

Robolectric.buildActivity(YourActivity.class).withIntent(intent).create(bundle) // and so on...
于 2014-02-17T18:33:46.073 回答
10

您正在针对哪个 Android API 级别进行编译?如果它是 3.0 或更高版本,您可以尝试Activity.recreate(). 该文档指出:

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

不过自己没试过。

于 2011-07-19T21:24:06.950 回答
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();

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

于 2014-10-20T14:38:43.553 回答
4

Robolectric 的 ActivityController 类有一个 configurationChange() 方法可能会处理这个问题。见鬼,它甚至还有 javadoc 注释!:D

于 2017-01-08T17:23:06.317 回答