版本记录
版本号 | 时间 |
---|---|
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,方法,
header
,cookie
和正文。 对JSON
和XML
支持。 - 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
已启动并正在运行。 您还会注意到WireMock
在Vendor
中创建了两个目录:__ files
和mappings
。
默认情况下,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调用,感兴趣的给个赞或者关注~~~