UITests中的模拟网络请求

联网是iOS开发人员必须面对的长期问题。 几乎没有应用程序,不需要它。 在测试中,我们需要可重现且稳定的结果,而网络对此不做任何保证。 理想情况下,我们希望将Mac抛入地下100m的暗洞中并运行自动化测试。 没有网络连接不应该成为测试失败的原因。 由于应用程序依赖于从后端获取数据,因此这需要我们方面的一些工作。

单元测试的存根网络调用中,我们确保控制了单元测试。 作为一个不错的副作用,它还提供了暗洞选项。 因此,无事可做。 概括地说,OHHTTPStubs用于返回NSURLRequest级别的数据。 遗憾的是,此技巧不适用于任何不与应用程序共享流程的UITest框架(EarlGrey可以工作;)。

继续使用OHHTTPStubs进行UI测试

您的第一个想法可能是:“为什么不在应用程序中使用OHHTTPStubs?” 这是可能的,但是有一些我不喜欢的原因:

  1. 很难控制需要退还什么。
  2. 它改变了应用程序内的许多逻辑。
  3. 它添加(测试)代码,该代码不属于生产代码。

控制后端

在编写UI测试时,您需要考虑要测试的内容。 您是要仅测试您的应用程序,还是要测试系统? 我工作过的公司倾向于测试整个产品。 这只是一个拐杖,因为他们对后端的正常工作没有信心。

测试您的系统听起来不错,但是错误并不能真正告诉您什么地方坏了。 您将不得不四处看看。 所以我建议 为您的应用程序使用UI测试。 为此,您必须为测试创建自己的后端。 它必须在同一台计算机上运行,​​因此不存在网络依赖性。 在一台机器上设置整个后端听起来有些矫kill过正,所以让我们跳过这一点。 相反,我建议创建一个模拟后端服务器,您可以在该服务器上控制测试所需的响应。

模拟后端的基本结构

我们对测试后端有什么要求?

  1. 我们可以控制,它返回什么
  2. 它在我们的应用程序之外运行
  3. 最小化我们的生产代码

这些只能通过编写我们自己的后端来实现。 最基本的想法是为每个请求类型提供一个带有字典的程序。 该词典将路径映射到响应。 因此,每当我们调用“ GET / hello”时,它将返回“ 200 OK – world”。

如果您希望将此后端编写为独立应用程序,则可以这样做。 也许有某种网络API,例如:

“ / getRoute?path = hello&result = world”

这将很容易实现。 只需为每个请求类型提供一个字典[String:String],然后将路径映射到响应即可。

我看到了这样一个独立后端的一些问题:

  • 您需要确保它在UI测试运行之前正确启动
  • 更改路径需要网络客户端
  • 我认为不必要的复杂性
  • 可能不是所有iOS开发人员都可以使用的语言

但是有时这些问题不是问题,甚至不是优势。 想象一下无法使用XCUITest而是使用某种外部工具。 您将不得不从测试代码之外创建和控制它。

感谢许多勤奋的开发人员,我们不仅可以将Swift用于应用程序,而且可以在后端使用。 那么,为什么不使用它并使iOS开发人员使用与应用程序相同的语言编写后端呢? 优点是:

  • 开发人员没有新的语言
  • 所有应用程序开发人员都可以在必要时进行处理
  • 从XCUITest内部开始,您可以在代码中对其进行配置

设置后端

为了实现我们的后端,我们将使用Swifter。 它是一个微型HTTP引擎,您可以在其中快速创建功能齐全的Web服务器。 有趣的是,它的行为与我们上面描述的内部结构非常相似。

要配置响应,您只需通过以下方法在内部进行设置

  server.GET [path] = HttpResponse.ok(响应) 

在UI测试中,我们可以启动服务器,对其进行配置,然后再启动应用程序。 当然,它提供了在测试过程中动态切换我们的响应的可能性。 让我们看一些伪代码:

  func test1(){ 
mockServer.configure()
mockServer.launch()
app.launch()
executeTest()
}

一个不错的好处是,服务器仅在测试时才存在。 如果macOS请求您允许网络连接(通常的防火墙屏幕),则可以创建规则并永久允许连接,停用防火墙(我不建议这样做)或忽略消息。 有趣的是,当对话框打开且您尚未拒绝它时,连接仍在建立。

应用程式

剩下的唯一开放问题是应用程序。 由于服务器在本地运行,因此您需要向您的应用程序添加一个新环境,该环境会将所有网络请求重定向到该服务器。 Yuri Chukhlib写了一篇有关处理应用程序环境的精彩文章。 因此,如果您不知道如何处理这些问题,请在继续之前先去那里。

要使用我们的模拟服务器,我们必须添加另一个环境。 它的基本URL为“ 127.0.0.1”,因为它应该连接到我们本地运行的服务器。 您可能不希望仅出于测试目的而重新编译应用程序,具体取决于发布过程。 因此,您需要以某种方式更改应用程序正在使用的环境。 我研究过的某些应用程序有一个秘密的手势可以进入调试菜单。 在此范围内,您可以更改环境。 大多数情况下,开发人员会回避此选项,因为他们担心用户会找到它们(这只是找到一个隐藏的好地方的问题……但仍然如此)。 因此,除了这样做,我们还可以将启动参数传递给应用程序。

您可以在测试代码中执行以下操作:

  app.launchArguments.append(“ USE_MOCK_SERVER”) 

在应用程序中,我们可以检查以下启动参数:

 如果ProcessInfo.processInfo.arguments.contains(“ USE_MOCK_SERVER”){ 
}

这是我们在生产代码中要做的唯一更改。 您可以将此技术用于应用程序内的其他必要更改,但不要过度使用它。 我不建议您在应用程序内添加不同的状态(例如“让我们使用此参数使应用程序认为已登录”)。 取而代之的是让测试运行更长的时间,并减少生产代码。 代码越多,代码就越复杂。 因此,请使其尽可能简单。

结论

引入此模拟服务器后,我们可以在黑暗的洞穴中运行测试。 有趣的是,它帮助我的团队更好地了解了我们的网络请求。 突然,我们不得不知道在哪个时间进行了哪些呼叫。 这也加快了我们的测试速度 时间相当可观。 总而言之,我只能建议您对整个网络进行打桩。 它提高了您的测试稳定性和速度。

下一页:屏幕对象

Previous:iOS应用程序的自动UI测试