在 Android 仪器测试中模拟资源



我想用真实设备测试我的程序的库调用的效果。此调用启动一个服务,该服务将 HTTP 请求发送到其 URL 在资源中硬编码的服务器。

我想验证请求是否正确发送。所以我设置了一个本地 HTTP 服务器,但为了能够使用它,我必须更改/覆盖/模拟资源,使其指向http://127.0.0.1

我想做"端到端"测试;在这种情况下,重要的是服务发出实际的网络请求,尽管是本地的。

我试图通过在 androidTest/res/values/strings.xml 中创建具有相同名称的字符串资源来覆盖该值,但该资源仅在测试包中可见,而在应用程序包中不可见。

使用 Instrumentation 类只允许我获取Context引用,但无法用模拟或类似的东西替换它(或 getResources() 的返回值(。

如何更改受测应用程序的资源值?

您有以下几种选择:

  • 依赖注入
  • 存根/模拟
  • 共享首选项
  • 脚本或分级任务

依赖注入

使用像RoboGuice或Dapper这样的库。注入处理发出 API 请求的对象。然后,在测试设置中,您可以将注入模块替换为测试版本。这样,您的测试代码就会运行而不是原始代码;该代码可以传入不同的字符串(硬编码或从测试字符串.xml传递(。

DI 库的设置成本可能很高:学习曲线高,如果使用不当,可能会出现性能问题。或者,如果对象的范围/生存期配置不正确,甚至可能会引入难以调试的问题。如果测试是使用 DI 的唯一原因,那么如果您对 DI 容器不满意,则可能不值得这样做。

存根/模拟

将您的

调用包装在实现您编写的自定义接口的东西中。然后,您的主实现填写主机 URL 并调用 API。然后,在测试中,使用该接口上的存根或模拟的组合来替换填充主机 URL 部分的代码。

这不是一个集成测试,因为存根或模拟将替换部分代码。但比设置依赖注入框架更简单。

共享首选项

使用 Android SharedPreferences 系统。将其默认为某个终结点(生产(。但允许在测试设备上启动应用,然后进行一些对话框或设置,以便更改主机 URL。再次运行测试,现在它们指向不同的 API URL。

脚本或分级任务

在某些情况下,编写一些脚本或 gradle 任务来修改源代码,然后再对其进行编译。

这可能相当复杂,如果做得不好,甚至可能过于依赖平台或系统。对系统的变化可能相当脆弱。如果运行错误的命令来构建最终打包版本并且错误的代码进入市场,则可能会引入错误。

个人意见

推荐哪个?如果您和/或您的团队熟悉像 RoboGuice 或 Dapper 这样的 DI 库,我推荐该选项。这是最正式、类型安全和严格的解决方案。它还保持了堆栈的更多完整性,以测试整个解决方案。

如果您不熟悉一个好的 DI 库,存根/模拟和接口包装器是一个很好的后备解决方案。无论如何,它们部分必须在 DI 解决方案中使用,您可以围绕它们编写足够的测试,以涵盖您需要测试(并且控制(的大多数情况。它与 DI 解决方案非常接近,我会向所有在项目中不使用 DI 的人推荐这个。

共享首选项解决方案非常适合在过渡环境和生产环境之间切换,以实现 QA 和支持。但是,我不建议将其用于自动测试,因为该应用程序很可能会在开发过程中经常重新安装/重置,因此经常重置该 URL 会很烦人。此外,首次运行测试可能会失败;CI 服务器上的无头测试会失败等(您可以将 URL 默认为本地主机,但随后您将冒着在某个时候意外将该默认值发布到生产环境的风险。

我不推荐脚本或被黑的 gradle 任务。太脆了,对你身后的其他开发人员来说不太清楚,而且比它们的价值更复杂,IMO。

除了Jon Adams的解决方案之外,还有一个解决方案:

重写生成类型中的资源

默认情况下,库模块在被另一个模块使用时以发布模式构建。调试模式仅用于测试(单元测试和插桩测试(。因此,使用资源重写可以仅更改该库的检测测试的资源值,并在库的用户中使用原始值。

不过,这有一些注意事项:

  • 插桩/集成测试必须保留在库本身上,而不是主应用程序包上;
  • 必须在所有测试之间共享相同的资源值(除非使用产品变种(

最新更新