我正在构建一个 iOS 框架,该框架收集有关 iOS 应用程序中各种事件的信息并执行本地和远程分析。其中一些事件无法在应用程序外部进行测试:例如, View Controller 转换。为了测试这些事件,我构建了一个测试 iOS 应用程序,并希望使用 Xcode UI 测试来:
通过点击将 VC 推送到导航 Controller 的按钮或切换到选项卡 Controller 中的不同选项卡来启动 View Controller 转换
验证框架是否能够检测 View Controller 转换并生成必要的事件(例如,将它们发送到服务器)
问题是 Xcode UI 测试在应用程序外部运行,因此我无法访问任何共享对象来验证框架是否正常运行。我也无法插入任何模拟对象,因为我无权访问应用程序进程中加载的框架。
我尝试以一种测试模式加载框架,并让它更新一个标签,其中包含 UI 测试可以读取的一些结果。但即便如此也很困难,因为 XCUIElement
没有给我元素的实际文本;我只能查询具有某些预定义文本的元素。我可以处理这个问题,但这对于一些本来应该相当微不足道的事情来说似乎是一种非常迂回的方法。
是否有更好的替代方法来测试只能在正在运行的应用程序中模拟的事件,然后验证我的框架是否能够检测和处理它们?除了 View Controller 转换之外,我还对触摸交互、加速计事件以及无法在应用程序外部模拟的其他功能感兴趣。当然,我可以模拟这些事件进行单元测试,但我还想自动测试当这些事件发生在实际应用程序中时,我的框架中会生成正确的响应。
最佳答案
您可以尝试使用 SBTUITestTunnel 。该库扩展了 UI 测试的功能,添加了一些在像您这样的情况下可能会派上用场的功能。
网络监控
该库允许您的测试目标收集应用程序中调用的网络调用。用法非常简单:
func testThatNetworkAfterEvent() {
// Use SBTUITunneledApplication instead of XCUIApplication
let app = SBTUITunneledApplication()
app.launchTunnelWithOptions([SBTUITunneledApplicationLaunchOptionResetFilesystem]) {
// do additional setup before the app launches
// i.e. prepare stub request, start monitoring requests
}
app.monitorRequestsWithRegex("(.*)myserver(.*)") // monitor all requests containing myserver
// 1. Interact with UI tapping elements that generate your events
// 2. Wait for events to be sent. This could be determined from the UI (a UIActivitiIndicator somewhere in your app?) or ultimately if you have no other option with an NSThread.sleepfortimeinterval
// 3. Once ready flush calls and get the list of requests
let requests: [SBTMonitoredNetworkRequest] = app.monitoredRequestsFlushAll()
for request in requests {
let requestBody = request.request!.HTTPBody // HTTP Body in POST request?
let responseJSON = request.responseJSON
let requestTime = request.requestTime // How long did the request take?
}
}
网络监控的好处是所有测试代码和逻辑都包含在您的测试目标中。
自定义代码块
还有其他用例,您需要执行自定义代码以便在测试目标中方便地调用,您也可以这样做。
在应用目标中注册一段代码
SBTUITestTunnelServer.registerCustomCommandNamed("myCustomCommandKey") {
injectedObject in
// this block will be invoked from app.performCustomCommandNamed()
return "any object you like"
}
并从测试目标调用它
func testThatNetworkAfterEvent() {
let app = ....
// at the right time
let objFromBlock = app.performCustomCommandNamed("myCustomCommand", object: someObjectToInject)
}
目前您只能从测试目标 -> 应用目标注入(inject)数据。如果您需要相反的方式,您可以将该数据存储在 NSUserDefaults 中,并使用 SBTUIApplication 的 userDefaultsObjectForKey()
方法获取它。
我个人不喜欢在应用程序的目标中混合标准代码和测试代码的想法,因此我建议仅在真正需要时才使用它。
编辑
我已经更新了库,从版本 0.9.23 开始,您现在可以将任何对象从 block 传递回测试目标。不再需要解决方法!
关于ios - 使用 Xcode UI 测试来测试底层框架行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38628745/