使用Xcode UI测试来测试底层框架行为

时间:2016-07-28 06:36:12

标签: ios xcode xcode-ui-testing ios-frameworks

我正在构建一个iOS框架,用于收集有关iOS应用程序中各种事件的信息,并执行本地和远程分析。其中一些事件无法在应用程序之外进行测试:例如,查看控制器转换。为了测试这些事件,我构建了一个测试iOS应用程序,并希望使用Xcode UI测试:

  • 启动视图控制器转换,例如,点击将VC推送到导航控制器的按钮,或切换到选项卡控制器中的其他选项卡

  • 验证框架是否能够检测到视图控制器转换并生成必要的事件(例如,将它们发送到服务器)

问题是Xcode UI测试在应用程序外部运行,因此我无法访问任何共享对象来验证框架是否正常运行。我也无法插入任何模拟对象,因为我再也无法访问应用程序进程中加载​​的框架。

我尝试以某种测试模式加载框架并让它更新标签,其中包含一些UI测试可以读取的结果。但即使这样也很难,因为XCUIElement没有给我实际的元素文本;我只能查询带有一些预定义文本的元素。我可以使用它,但对于一些应该相当微不足道的东西,它似乎是一种非常迂回的方法。

是否有更好的替代方法来测试只能在正在运行的应用中模拟的事件,然后验证我的框架是否能够检测并处理它们?除了查看控制器转换之外,我还对触摸交互,加速度计事件以及无法在应用程序外部模拟的其他功能感兴趣。当然,我可以模拟这些事件进行单元测试,但我还想自动测试,当这些事件发生在实际应用程序中时,我的框架中会生成正确的响应。

2 个答案:

答案 0 :(得分:1)

您可以尝试使用SBTUITestTunnel。这个库扩展了UI测试的功能,添加了一些在像你这样的情况下可能派上用场的功能。

网络监控

该库允许您的测试目标收集在应用程序中调用的网络调用。用法很简单:

readline

网络监控的好处是所有测试代码和逻辑都包含在测试目标中。

自定义代码块

在其他用例中,您需要执行自定义代码才能在测试目标中方便地调用,您也可以这样做。

在应用目标

中注册代码块
undef

并从测试目标

调用它
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?
  }
}

目前,您只能从测试目标中注入数据 - >应用目标。如果您需要相反的方法,可以将该数据存储在NSUserDefaults中,并使用SBTUIApplication的SBTUITestTunnelServer.registerCustomCommandNamed("myCustomCommandKey") { injectedObject in // this block will be invoked from app.performCustomCommandNamed() return "any object you like" } 方法获取它。

我个人不喜欢在您的应用程序目标中混合使用标准代码和测试代码,因此我建议仅在真正需要时才使用它。

修改

我已经更新了库并从愿景0.9.23开始,您现在可以将任何对象从块传递回测试目标。不再需要解决方法了!

答案 1 :(得分:0)

完成任务并不是一种自动化的方法,但确实使用断点作为中间方式,至少在您自动启动之前。您可以添加符号断点来点击UI方法,例如[UIWindow setRootViewController:],viewWillAppear等。

您可以明确定义模块,条件和操作,以便每当有人查看您的VC时,都会执行一些可能对您有所帮助的操作。

如果你可以在一些viewDidAppear上使用简单的“po myEvent”从你的框架中打印出一些东西,你就可以了。我从未尝试过使用更高级的方法作为动作,但也可以这样做。