ios - XCUITest:如何跳转到应用程序代码?如何修改被测应用的状态?
问题描述
来自 Android/Espresso 背景,我仍在为 iOS 的 XCUITest 和 UI 测试而苦苦挣扎。我的问题是关于两个相关但不同的问题:
- 如何编译和链接被测应用程序的源代码?
- 如何跳转到被测应用的方法并在运行时修改其状态?
要解决这些问题,我们首先要了解 XCode 的“单元测试目标”和“UI 测试目标”之间的区别。
XCUITests 在一个完全独立的进程中运行,不能跳转到被测应用程序的方法中。此外,默认情况下,XCUITests 不会链接到被测应用程序的任何源。
相反,XCode 的单元测试与应用程序源相关联。还可以选择执行“@testable 导入”。理论上,这意味着单元测试可以跳转到任意应用程序代码中。但是,单元测试不会针对实际应用程序运行。相反,单元测试是针对没有任何 UI 的精简版 iOS SDK 运行的。
现在这些约束有不同的解决方法:
将一些选定的源文件添加到 UI 测试目标。这不能调用应用程序,但至少可以在应用程序和 UI 测试之间共享选定的代码。
通过
CommandLine.arguments
UI 测试将启动参数传递给被测应用程序。这可以将特定于测试的配置应用于被测应用程序。但是,这些启动参数需要由应用程序解析和解释,这会导致应用程序被测试代码污染。此外,启动参数只是改变被测应用程序行为的一种非交互方式。实现只有 XCUITest 才能访问的“调试 UI”。同样,这有污染应用程序代码的缺点。
这引出了我的结论性问题:
存在哪些替代方法可以使 XCUI 测试更强大/动态/灵活?
我可以针对整个应用程序源和所有 pod 依赖项而不是仅几个选定的文件编译和链接 UI 测试吗?
是否有可能获得 Android 的仪器测试 + Espresso 的强大功能,我们可以在其中对被测应用执行任意状态修改?
为什么我们需要这个
作为对@theMikeSwan 的回应,我想澄清我对 UI 测试架构的立场。
UI 测试不需要链接到应用程序代码,它们旨在模拟用户在您的应用程序内轻敲。如果您在这些测试期间跳入应用程序代码,您将不再测试您的应用程序在现实世界中的作用,您将测试它在以用户无法做到的方式操作时的作用。UI 测试不应该比用户更需要任何应用程序代码。
我同意以这种方式操作应用程序是一种反模式,只应在极少数情况下使用。然而,我对什么应该是可能的有非常不同的立场。在我看来,UI 测试的正确方法不是黑盒测试,而是灰盒测试。尽管我们希望 UI 测试尽可能地黑盒化,但在某些情况下,我们希望深入挖掘被测应用程序的实现细节。只是给你几个例子:
可扩展性:没有任何 UI 测试框架可以为每个用例提供 API。项目要求不同,有时我们想编写自己的函数来修改应用程序状态。
内部状态断言:我希望能够为应用程序的状态编写自定义断言(不仅依赖于 UI 的断言)。在我当前的 Android 项目中,我们有一个臭名昭著的损坏的子系统。我用自定义方法断言这个子系统以防止回归错误。
共享模拟对象:在我当前的 Android 项目中,我们有自定义硬件不可用于 UI 测试。我们用模拟对象替换了这个硬件。我们直接从 UI 测试中对这些模拟对象运行断言。这些断言通过共享内存无缝工作。此外,我不想用所有的模拟实现污染应用程序代码。
将测试数据保留在外部:在我当前的 Android 项目中,我们将测试数据从 JUnit 直接加载到应用程序中。使用 XCUITest 的命令行参数,这将受到更多限制。
自定义同步机制:在我当前的 Android 项目中,我们有围绕多线程基础设施的包装类,以将我们的 UI 测试与后台任务同步。如果没有共享内存(例如 Espresso IdlingResources),这种同步很难实现。
琐碎的代码共享:在我当前的 iOS 项目中,我为上述启动参数共享了一个简单的定义文件。这使得能够以类型安全的方式传递启动参数,而无需复制字符串文字。虽然这是一个小用例,但它仍然表明选定的代码共享可能很有价值。
对于 UI 测试,您不应该过多地污染您的应用程序代码。您可以使用单个命令行参数来指示 UI 测试正在运行,并使用它来加载一些测试数据、登录测试用户或选择网络调用的测试端点。有了良好的架构,您只需在应用程序首次启动时进行一次调整,而其余代码却没有注意到它正在使用测试数据(就像您有一个开发环境和一个生产环境,您可以在网络调用之间切换)。
这正是我在当前 iOS 项目中正在做的事情,而这正是我想要避免的事情。虽然一个好的架构可以避免太多的破坏,但它仍然是对应用程序代码的污染。此外,这并不能解决我上面强调的任何用例。通过提出这样的解决方案,您基本上承认激进的黑盒测试不如灰盒测试。在生活的许多方面,差异化的观点比激进的“只使用我们给你的工具,你不应该这样做”要好。
解决方案
推荐阅读
- discord.js - discord.js 模块问题使机器人崩溃
- video - x264 / libx264 : 只能将一个 I/P 帧用作 B 帧的参考吗?
- python - MongoDB - $set 和 $addToSet 之间的优先级
- javascript - 尝试对用户创建的输入值求和
- flutter - 如何在没有任何包的情况下访问音频元数据?
- c++ - 在这种情况下,调用我的函数之前 std::cout 会做什么?
- sql - 在过滤后仅选择 SQL 中的最小值包括变量?
- hyperledger-fabric - getHistoryForKey 的历史从何而来?
- c++ - 为什么我的小于运算符没有处理?
- sql - Oracle:将 DD-MM-YYYY 转换为 MON-YYYY