WireMock详细解析(一) —— Xcode中基于WireMock和UI Tests的本地API调用(一)

版本记录

版本号 时间
V1.0 2019.08.12 星期一

前言

WireMock是基于HTTP的API的模拟器,下面我们就一起来学习和了解。

开始

首先看下主要内容

了解如何使用WireMock,这是一种可以与用户界面测试结合使用的工具,用于提供远程API调用结果的本地副本。

然后看一下写作环境

Swift 5, iOS 13, Xcode 11

WireMock是基于HTTP的API的模拟器。 有些人可能会认为它是服务虚拟化(service virtualization)工具或模拟服务器(mock server)。可以看一下WireMock官网,和 GitHub - WireMock

当您依赖的API不存在或不完整时,它可以使您保持高效。 它支持测试真实API无法可靠生成的边缘情况和失败模式。 而且因为速度快,它可以将构建时间从几小时减少到几分钟。

它具有以下几个特点:

  • Flexible Deployment:从Java应用程序,JUnit测试,Servlet容器或独立进程中运行WireMock
  • Powerful Request Matching:使用各种策略匹配请求URL,方法,headercookie和正文。 对JSONXML支持。
  • Record and Playback:通过捕获现有API的流量来快速启动并运行。

WireMock是一个开发工具,它提供远程API调用结果的本地副本。 您可以将它与User Interface(UI)测试结合使用,以呈现不同类型的API响应。

UI测试充当安全网,确保您提供预期的用户体验。 它侧重于您的应用程序的端到端体验。 同样,UI测试可以回答不同于单元测试的问题,例如:

  • 如果我的视图为空,在此屏幕上会发生什么?
  • 如果我从网络请求中收到错误会怎么样?
  • 我的验证用户界面是否显示了不同类型输入的正确信息?
  • 我的table cells是否反映了收到的数据?

首先,找到入门项目并打开它。 构建并运行项目以检查功能。

星球大战(Star Wars)信息应用程序使用Star Wars API来检索星球大战角色列表。 您可以点击每个单元格以导航到该角色的详细信息页面。

1. Running the Tests

该应用程序已经设置了UI测试目标(UI testing target)和两个UI测试。 UI测试目标已连接到构建方案的默认Test操作。

通过按Command-U或单击菜单项Product ▸ Test来运行测试。 您将看到应用程序启动并从列表视图导航到详细视图并返回。 在此导航期间,UI测试验证列表视图和详细信息视图上的预期条件。

要查看测试报告,请转到左侧窗格中的Report navigator,然后查看测试结果:

只要您的计算机处于联机状态并且测试可以成功点击Star Wars API,测试就会通过。

2. UI Test Code

查看StarWarsInfoUITests.swift以查看UI tests。 UI测试的详细信息超出了本文的范围。

示例项目演示了最佳实践,包括:

  • 尽可能使用辅助功能标识符而非辅助功能标签labels来标识元素。这种方法的好处包括:
    • 用户无法以任何方式使用辅助功能标识符,因此您可以根据需要为其命名。
    • 标识符保持不变。例如,label可能具有不同的值,但其标识符保持不变。所以你的测试仍然可以在屏幕上找到元素。
  • XCTestCase上的扩展中名为waitForElementToAppear(_:file:line:elementName:timeout :)的便捷方法允许自定义超时。
  • 将文件和行号传递给便利函数允许UI测试系统报告测试失败。

Challenges With the Current Setup

这个小应用程序和相关的测试在当前配置中表现良好,但随着代码库的增长,应用程序将依赖于更多的网络请求。 测试会变慢并变得不稳定。

以下是代码库增长时可以预见的一些问题:

  • 如果您依赖的网络请求失败,会发生什么?
  • 如果网络速度变慢,导致超时或延长等待时间,会发生什么?
  • 如果列表请求开始以不同的排序顺序发回数据怎么办?

在所有三种情况下,您的测试都会失败。 有一个共同点。 这些测试失败都不是由编程错误或逻辑问题引起的。 在这些情况下测试失败将是误报,因为失败将超出您的控制范围。

1. Mitigating Network Failures

在本教程中,您将消除UI测试中的一个不确定性来源。 您将停止制作实时网络请求并依赖称为WireMock的网络模拟技术。 WireMock允许您将网络排除为UI测试中潜在的故障源。

使用WireMock,您将在每次测试运行时始终获得与给定请求相同的响应。 这使您可以更自信地进行测试。 如果出现failure,您将花费更少的时间来确定原因。 网络是任何应用程序中更易变的组件之一。 通过将其从失败的潜在原因列表中删除,您将能够专注于您尝试使用每个屏幕测试的基本功能。


Overview of WireMock

WireMock是一个开源库,允许您运行本地Web服务器进行测试。通过在运行测试时更新您从应用程序调用的API端点,您可以返回已知的响应。这可以帮助您在开发过程中更快地进行。在测试运行期间,将应用程序指向WireMock实例允许您从API请求接收预期的响应。

足够的理论。开始的时候了!


Setting Up WireMock

WireMock至少需要Java 7,因此请确保您的Mac安装了更新版本的Java。从Oracle下载最新的Java Development Kit JDK。您将作为独立实例运行WireMock。转到下载页面download page,获取WireMock的最新稳定版本。下载将是一个JAR文件。

转到starter项目的基本目录,并创建名为Vendor的目录。将JAR文件移动到此目录中。重命名JAR文件WireMock.jar,以便更容易地从命令行进行交互。接下来,打开终端并导航到您创建的Vendor目录。

在此目录中,键入以下命令以启动WireMock进程:

java -jar wiremock.jar --port 9999 --verbose

此命令在端口9999上启动WireMock并打开详细模式(verbose mode)。 打开详细模式可以让您在应用与WireMock端点交互时查看所有消息,包括URL匹配和未命中。 在尝试确定WireMock端点可能无法正常工作的原因时,这是一个很大的帮助。

现在,测试WireMock是否在您的系统上运行。 打开浏览器并转到http://localhost:9999/__admin/mappings

你应该看到以下内容:

{
  "mappings" : [ ],
  "meta" : {
    "total" : 0
  }
}

很好! 这意味着WireMock已启动并正在运行。 您还会注意到WireMockVendor中创建了两个目录:__ filesmappings

默认情况下,WireMock的独立实例会查找与可执行文件位置相关的这两个目录。

  • __files目录包含您要为任何给定的API请求发回的模拟响应。
  • mappings目录包含将API请求模式映射到包含所需响应的特定文件的文件。

Setting Up a Mock Response

示例应用程序使用Star Wars API中的一个端点来请求字符列表。 该端点是https://swapi.co/api/people/

如果您在浏览器中访问此端点,Star Wars API页面可以很好地向您显示JSON响应,包括headers。 您只对这里的回复感兴趣。

复制不包括headers的完整response

{
  "count": 87,
  "next": "https://swapi.co/api/people/?page=2",
  "previous": null,
  "results": [
    {
      "name": "Luke Skywalker",
      "height": "172",
      "mass": "77",
      "hair_color": "blond",
      "skin_color": "fair",
      "eye_color": "blue",
      "birth_year": "19BBY",
      "gender": "male",
      // Clipped for brevity here
  ]
}

将此响应作为character-list.json保存到__files目录中。

1. Setting Up Mappings

接下来,您需要让WireMock知道如何将people端点映射到此响应。 在名为 character-list.json的映射目录中创建一个新文件。

使文件的内容如下所示:

{
  "request": {
      "method": "GET",
      "url": "/swapi.co/api/people"
  },
  "response": {
      "status": 200,
      "bodyFileName": "character-list.json"
  }
}

这使得Wiremock知道使用/swapi.co/api/people路径将任何GET请求映射到具有HTTP 200成功状态的character-list.json响应。

您的最终Vendor目录布局应如下所示:

2. Verify Functionality

现在,验证WireMock是否已将您的映射编入索引。 如果您的终端仍然在上一个会话中运行,请按Control-C停止它,然后使用以下命令再次启动WireMock

java -jar wiremock.jar --port 9999 --verbose

WireMock应该已经拿起你的新映射文件。 要检查这一点,请转到http://localhost:9999/__admin/mappings

您应该看到以下输出:

{
  "mappings" : [ {
    "id" : "5804b634-2a15-4fb0-a16e-2c19559f37df",
    "request" : {
      "url" : "/swapi.co/api/people",
      "method" : "GET"
    },
    "response" : {
      "status" : 200,
      "bodyFileName" : "character-list.json"
    },
    "uuid" : "5804b634-2a15-4fb0-a16e-2c19559f37df"
  } ],
  "meta" : {
    "total" : 1
  }
}

此输出表示WireMock正在拾取您的映射文件。 接下来,检查为端点返回的响应。 转到浏览器并输入http://localhost:9999/swapi.co/api/people

您应该看到以下输出:

很棒!一切都在WireMock方面发挥作用。现在,您将把WireMock集成到UI test中。


Integrating WireMock

要将WireMock的工作实例集成到UI测试中,请按照下列步骤操作:

  • 1) 设置项目以允许来自本地运行的WireMock实例的HTTP加载。
  • 2) 使用启动参数启动UI测试以传递已定义的参数。
  • 3) 创建一种基于启动参数在代码中切换URL schemes的方法。

1. Allowing Loads from a Local HTTP Server

首先,配置项目以允许来自非SSL连接的HTTP请求。如果您不执行此步骤,App Transport Security(ATS)将不允许网络请求连接到WireMock

通常不建议允许不安全的负载,但对于UI测试,它比配置自签名证书更容易从WireMock启用SSL。在更复杂的设置中,您可以将项目配置为仅允许测试方案中的不安全负载使用构建配置设置或测试目标的不同Info.plist

按照以下步骤使用WireMock设置ATS进行UI测试:

  • 1) 在StarWarsInfo group组中打开Info.plist
  • 2) 在底部,单击最后一行上的+
  • 3) 从Key列的下拉列表中选择App Transport Security Settings
  • 4) 按Tab键。
  • 5) 选择右侧的显示三角形使其指向下方。
  • 6) 现在,在App Transport Security Settings行中单击+
  • 7) 从新行的下拉列表中,选择Allow Arbitrary Loads
  • 8) 最后,在此行的Value列中,将值更改为YES

2. Defining a Launch Argument

接下来,您将定义一个启动参数,让应用程序知道何时切换URL schemes。 从Xcode启动时,您可以将参数传递给您的应用程序,类似于将参数传递给终端命令的方式。 在Xcode中使用Scheme Editor

您将在该scheme的运行操作中定义启动参数。 通过这种方式配置,您可以在模拟器中运行应用程序而无需运行任何测试。 一旦确定一切都按照您想要的方式运行,您将在测试代码中定义相同的参数并使用它们启动应用程序。 运行UI测试时,应用程序的行为方式应该相同。

3. Editing the Xcode Scheme

单击Xcode中的StarWarsInfo方案,然后单击Edit Scheme

在左侧窗格中选择Run操作。 在中间窗格中,选择Arguments选项卡。 在Arguments Passed on Launch部分中,单击+并添加启动参数-runlocal。 确保选中新启动参数左侧的复选框。

单击Close接受更改并返回到您的代码。

4. Updating to Use Launch Arguments

现在,您需要更新代码库以查找此启动参数并使用适当的URL scheme

Adding a Utility Function

首先,创建一个实用程序方法来查找启动参数。 按住Control键并单击StarWarsInfo组,然后创建一个名为Utils的新组。 然后,按住Control键并单击新的Utils组,并创建一个名为StartupUtils.swift的新Swift文件。 将以下代码添加到此文件:

struct StartupUtils {
  static func shouldRunLocal() -> Bool {
    return CommandLine.arguments.contains("-runlocal")
  }
}

此代码检查CommandLine.arguments,它是一个字符串数组。 如果该数组包含-runlocal参数,则该函数将返回true

Checking for Local flag in Network Client

接下来,在网络客户端中使用新的便利函数。 在Network组内,打开StarWarsAPINetworkClient.swift。 将baseURLString的属性更新为计算属性,如下所示:

var baseURLString: String {
  if StartupUtils.shouldRunLocal() {
    return "http://localhost:9999/swapi.co/api/"
  } else {
    return "https://swapi.co/api/"
  }
}

如果启动时存在-runlocal属性,则baseURLString将使用local scheme,连接到正在运行的WireMock实例以获取其数据。 现在,您可以完全控制API的数据响应。

打开终端(如果尚未打开)并确保WireMock正在运行。 导航到包含WireMock可执行文件的目录。 运行相同的命令以启动WireMock

java -jar wiremock.jar --port 9999 --verbose

Perform a Run with WireMock

设置启动参数,按Command-R建立并运行。 加载初始列表时观察终端。 当应用程序请求网络数据并且WireMock返回时,您应该看到控制台打印输出。

5. Testing Against WireMock

现在,执行最后的步骤,使用正确的启动参数设置UI测试。

UI Test Launch Arguments

回到Xcode,展开Project导航器中的StarWarsInfoUITests组。 然后按住Control键并单击Utility组。 创建一个名为LaunchArguments.swift的新文件。 请务必选择StarWarsInfoUITests并从Save As对话框的Targets部分中删除StarWarsInfo的选择。

添加以下代码

struct LaunchArguments {
  static var launchLocalArguments: [String] {
    return [
      "-runlocal"
    ]
  }
}

最后,展开项目导航器中的Tests组并打开StarWarsInfoUITests.swift。 在两个测试中的每个测试中,在创建本地app参数之后,添加以下行:

app.launchArguments = LaunchArguments.launchLocalArguments

这会为每个测试添加-runlocal启动参数。

现在,您可以测试针对WireMock而不是实时网络运行的UI测试。

切换回Xcode并按Command-U运行UI测试。 如果您在测试运行时观察终端,您将看到WireMock的输出作为您的测试请求数据,并且它们是从本地服务器返回的。

恭喜! 您已消除UI测试对网络的严重依赖。

除了模拟您从实时网络请求获得的相同响应之外,您现在可以将不同的场景发送回您的UI并查看应用程序的响应方式。

例如:

  • 如果收到HTTP 500状态,您的应用会显示正确的错误消息吗?
  • 如果UI收到空数据集,会发生什么?
  • 如果收到格式错误的数据集,应用程序是否以正确的方式响应?
  • 如果收到的某些字符串对于UI来说太长,会发生什么? 是否所有内容都按预期包装或截断?

尝试调整模拟的响应以测试应用内的不同流。 WireMock还允许您通过同一端点的许多不同变量来改变响应,例如查询参数和标头。 查看文档documentation以了解还有什么可能情况。

后记

本篇主要讲述了Xcode中基于WireMock和UI Tests的本地API调用,感兴趣的给个赞或者关注~~~

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容