Java 在Android instrumentation测试中模拟资源

Java 在Android instrumentation测试中模拟资源,java,android,android-testing,Java,Android,Android Testing,我想用一个真实的设备测试我的程序的库调用的效果。此调用启动一个服务,该服务向其URL在资源中硬编码的服务器发送HTTP请求 我想验证请求是否正确发送。因此,我设置了一个本地HTTP服务器,但为了能够使用它,我必须更改/覆盖/模拟资源,使其指向http://127.0.0.1 我想做“端到端”测试;在这种情况下,重要的是服务发出实际的网络请求,尽管是本地请求 我试图通过在androidTest/res/values/strings.xml中创建同名字符串资源来覆盖该值,但该资源仅在测试包中可见,在

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

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

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

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

使用
Instrumentation
类只允许我获取
上下文
引用,但无法将其(或
getResources()
的返回值)替换为mock或类似的内容


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

您有两种选择:

  • 依赖注入
  • 存根/模拟
  • 共享引用
  • 脚本或渐变任务
依赖注入 使用RoboGuice或Dapper之类的库。注入处理发出API请求的对象。然后,在测试设置中,您可以用测试版本替换注入模块。这样你的测试代码就可以代替原来的代码运行了;该代码可以传递不同的字符串(硬编码或来自teststrings.xml)

DI库的设置成本很高:学习曲线很高,如果使用不当,可能会导致性能问题。如果对象的作用域/生存期配置不正确,甚至可能会导致难以调试的问题。如果测试是使用DI的唯一原因,那么如果您不习惯使用DI容器,那么它可能不值得

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

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

共享引用 使用Android SharedReferences系统。将其默认为某个端点(生产)。但允许在测试设备上启动应用程序,然后通过一些对话框或设置来更改主机URL。再次运行测试,现在它们指向不同的API URL

脚本或渐变任务 在某些场景中编译源代码之前,编写一些脚本或gradle任务来修改源代码

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

个人意见 我推荐哪一种?如果您和/或您的团队熟悉RoboGuice或Dapper之类的DI库,我建议您选择该选项。它是最正式、类型安全和严格的解决方案。它还维护了堆栈的更多完整性,以测试整个解决方案

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

SharedPreferences解决方案非常适合在阶段和生产环境之间切换以获得QA和支持。然而,我不建议将其用于自动测试,因为在开发过程中,该应用程序很可能会频繁地重新安装/重置,而频繁地重置该URL会让人恼火。而且,第一次测试可能会失败;CI服务器上的无头测试可能会失败,等等(您可以将URL默认为本地主机,但您可能会在某个时候意外地将该默认值发布到生产环境中)


我不推荐脚本或被黑客攻击的gradle任务。太脆弱了,你后面的其他开发人员不太清楚,而且更复杂,所以他们值得,我想。

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

重写生成类型中的资源 当它被另一个模块使用时。调试模式仅用于测试(单元测试和仪器化测试)。因此,使用资源重写可以仅更改该库的检测测试的资源值,并在库的用户中使用原始值

不过,这有一些警告:

  • 插装测试/集成测试必须留在库本身,而不是主应用程序包上
  • 必须在所有测试中共享相同的资源值(除非使用产品风格)

我们已经在使用AndroidAnnotations,它不允许在AFAIK中更改生产代码(尽管它对于单元测试非常有用),并且在同一代码中使用两个DI框架可能会妨碍可维护性。我会避免SharedReference,因为这会让恶意用户更容易在生产环境中放置不同的URL。我想出了另一个对我有用的选项,我将作为答案发布。我们将url端点对话框隐藏在一个简单的4位代码后面。所以要改变它,用户必须联系我们。