Mocker 是一个用 Swift 编写的库,它通过自定义 URLProtocol
使数据请求的模拟成为可能。
功能
在不离开本地网络的情况下运行您所有的数据请求单元测试
- 基于 URL 创建模拟数据请求
- 基于文件扩展名创建模拟数据请求
- 与使用自定义协议类的
URLSession
配合工作 - 支持像
Alamofire
这样的流行框架
使用方法
为 Mocker
编写了单元测试,这可以帮助您了解其工作原理。
激活 Mocker
在注册第一个 Mock
之后,Mocker 将自动为默认的 URL 加载系统(如 URLSession.shared
)启动。
自定义 URLSessions
为了让它与您的自定义 URLSession
兼容,需要注册 MockingURLProtocol
let configuration = URLSessionConfiguration.default
configuration.protocolClasses = [MockingURLProtocol.self]
let urlSession = URLSession(configuration: configuration)
Alamofire
与在自定义 URLSession
上注册相当类似。
let configuration = URLSessionConfiguration.af.default
configuration.protocolClasses = [MockingURLProtocol.self]
let sessionManager = Alamofire.Session(configuration: configuration)
注册模拟
创建您的模拟数据
建议创建一个类,使所有模拟数据可访问。该项目单元测试中的示例。
public final class MockedData {
public static let botAvatarImageResponseHead: Data = try! Data(contentsOf: Bundle(for: MockedData.self).url(forResource: "Resources/Responses/bot-avatar-image-head", withExtension: "data")!)
public static let botAvatarImageFileUrl: URL = Bundle(for: MockedData.self).url(forResource: "wetransfer_bot_avater", withExtension: "png")!
public static let exampleJSON: URL = Bundle(for: MockedData.self).url(forResource: "Resources/JSON Files/example", withExtension: "json")!
}
JSON 请求
let originalURL = URL(string: "https://www.wetransfer.com/example.json")!
let mock = Mock(url: originalURL, dataType: .json, statusCode: 200, data: [
.get : try! Data(contentsOf: MockedData.exampleJSON) // Data containing the JSON response
])
mock.register()
URLSession.shared.dataTask(with: originalURL) { (data, response, error) in
guard let data = data, let jsonDictionary = (try? JSONSerialization.jsonObject(with: data, options: [])) as? [String: Any] else {
return
}
// jsonDictionary contains your JSON sample file data
// ..
}.resume()
忽略查询
一些如身份验证 URL 的网址查询中包含时间戳或 UUID。为了模拟这些,您可以忽略特定 URL 的查询。
/// Would transform to "https://www.example.com/api/authentication" for example.
let originalURL = URL(string: "https://www.example.com/api/authentication?oauth_timestamp=151817037")!
let mock = Mock(url: originalURL, ignoreQuery: true, dataType: .json, statusCode: 200, data: [
.get : try! Data(contentsOf: MockedData.exampleJSON) // Data containing the JSON response
])
mock.register()
URLSession.shared.dataTask(with: originalURL) { (data, response, error) in
guard let data = data, let jsonDictionary = (try? JSONSerialization.jsonObject(with: data, options: [])) as? [String: Any] else {
return
}
// jsonDictionary contains your JSON sample file data
// ..
}.resume()
文件扩展名
let imageURL = URL(string: "https://www.wetransfer.com/sample-image.png")!
Mock(fileExtensions: "png", dataType: .imagePNG, statusCode: 200, data: [
.get: try! Data(contentsOf: MockedData.botAvatarImageFileUrl)
]).register()
URLSession.shared.dataTask(with: imageURL) { (data, response, error) in
let botAvatarImage: UIImage = UIImage(data: data!)! // This is the image from your resources.
}.resume()
自定义HEAD和GET响应
let exampleURL = URL(string: "https://www.wetransfer.com/api/endpoint")!
Mock(url: exampleURL, dataType: .json, statusCode: 200, data: [
.head: try! Data(contentsOf: MockedData.headResponse),
.get: try! Data(contentsOf: MockedData.exampleJSON)
]).register()
URLSession.shared.dataTask(with: exampleURL) { (data, response, error) in
// data is your mocked data
}.resume()
延迟响应
有时你需要测试请求取消功能是否正常工作。在这种情况下,模拟请求不应直接完成,你需要延迟。这可以轻松地添加。
let exampleURL = URL(string: "https://www.wetransfer.com/api/endpoint")!
var mock = Mock(url: exampleURL, dataType: .json, statusCode: 200, data: [
.head: try! Data(contentsOf: MockedData.headResponse),
.get: try! Data(contentsOf: MockedData.exampleJSON)
])
mock.delay = DispatchTimeInterval.seconds(5)
mock.register()
重定向响应
有时你想模拟短URL或其他的重定向URL。这通过保存响应并模拟重定向位置,这个位置可以在响应内部找到来实现。
Date: Tue, 10 Oct 2017 07:28:33 GMT
Location: https://wetransfer.com/redirect
通过为短URL和重定向URL创建模拟,你可以模拟重定向并测试这种行为。
let urlWhichRedirects: URL = URL(string: "https://we.tl/redirect")!
Mock(url: urlWhichRedirects, dataType: .html, statusCode: 200, data: [.get: try! Data(contentsOf: MockedData.redirectGET)]).register()
Mock(url: URL(string: "https://wetransfer.com/redirect")!, dataType: .json, statusCode: 200, data: [.get: try! Data(contentsOf: MockedData.exampleJSON)]).register()
忽略URL
由于模拟器默认情况下捕获所有注册的URL,你可能不需要模拟请求却抛出fatalError
。在这种情况下,你可以忽略该URL。
let ignoredURL = URL(string: "www.wetransfer.com")!
Mocker.ignore(ignoredURL)
然而,如果你需要模拟器仅捕获模拟URL并忽略其他URL,你可以将mode
属性设置为.optin
。
Mocker.mode = .optin
如果你想将原始模式设置回默认,只需将其设置为.optout
即可。
Mocker.mode = .optout
模拟错误
你可以请求一个Mock
返回一个错误,允许测试错误处理。
Mock(url: originalURL, dataType: .json, statusCode: 500, data: [.get: Data()],
requestError: TestExampleError.example).register()
URLSession.shared.dataTask(with: originalURL) { (data, urlresponse, err) in
XCTAssertNil(data)
XCTAssertNil(urlresponse)
XCTAssertNotNil(err)
if let err = err {
// there's not a particularly elegant way to verify an instance
// of an error, but this is a convenient workaround for testing
// purposes
XCTAssertEqual("example", String(describing: err))
}
expectation.fulfill()
}.resume()
Mock回调
您可以在Mock回调上注册,以便使测试更加简单。
var mock = Mock(url: request.url!, dataType: .json, statusCode: 200, data: [.post: Data()])
mock.onRequest = { request, postBodyArguments in
XCTAssertEqual(request.url, mock.request.url)
XCTAssertEqual(expectedParameters, postBodyArguments as? [String: String])
onRequestExpectation.fulfill()
}
mock.completion = {
endpointIsCalledExpectation.fulfill()
}
mock.register()
Mock预期
除了设置completion
和onRequest
之外,您还可以使用预期。
var mock = Mock(url: url, dataType: .json, statusCode: 200, data: [.get: Data()])
let requestExpectation = expectationForCompletingMock(&mock)
let completionExpectation = expectationForCompletingMock(&mock)
mock.register()
URLSession.shared.dataTask(with: URLRequest(url: url)).resume()
wait(for: [requestExpectation, completionExpectation], timeout: 2.0)
通信
- 如果发现错误,请提交问题。
- 如果您有功能请求,请提交问题。
- 如果您想贡献,提交拉取请求。
安装
CocoaPods
CocoaPods 是Cocoa项目的依赖管理器。您可以使用以下命令来安装它:
$ gem install cocoapods
要使用CocoaPods将Mocker集成到您的Xcode项目中,请将其指定在您的Podfile
中
source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '10.0'
use_frameworks!
target '<Your Target Name>' do
pod 'Mocker', '~> 2.5.4'
end
然后,运行以下命令
$ pod install
Carthage
Carthage 是一个去中心化的依赖管理器,可以构建您的依赖并提供二进制框架。
您可以使用以下命令使用 Homebrew 安装 Carthage:
$ brew update
$ brew install carthage
要使用 Carthage 将 Mocker 集成到您的 Xcode 项目中,请在您的 Cartfile
中指定它。
github "WeTransfer/Mocker" ~> 2.3.0
运行 carthage update
以构建框架,并将构建的 Mocker.framework
拖放到您的 Xcode 项目中。
Swift Package Manager
Swift Package Manager 是一个用于管理 Swift 代码分发的工具。它与 Swift 构建系统集成,以自动化下载、编译和链接依赖项的过程。
清单文件
将 Mocker 作为包添加到您的 Package.swift
文件中,然后将其指定为 Target(您希望在其中使用它的目标)的依赖项。
import PackageDescription
let package = Package(
name: "MyProject",
platforms: [
.macOS(.v10_15)
],
dependencies: [
.package(url: "https://github.com/WeTransfer/Mocker.git", .upToNextMajor(from: "2.3.0"))
],
targets: [
.target(
name: "MyProject",
dependencies: ["Mocker"]),
.testTarget(
name: "MyProjectTests",
dependencies: ["MyProject"]),
]
)
Xcode
要将 Mocker 作为依赖项添加到您的 Xcode 项目中,请选择 File > Swift Packages > Add Package Dependency 并输入仓库 URL。
解决构建错误
如果您遇到以下错误:找不到自动链接库 XCTest 和 XCTestSwiftSupport,请将以下属性从无切换到是。
ENABLE_TESTING_SEARCH_PATHS 为是
手动
如果您不想使用上述任何依赖项管理器,您可以将 Mocker 手动集成到项目中。
嵌入式框架
-
打开终端,使用
cd
命令进入顶层项目目录,如果您的项目未初始化为Git仓库,请运行以下命令$ git init
-
通过运行以下命令将Mocker添加为Git子模块
$ git submodule add https://github.com/WeTransfer/Mocker.git
-
打开新的
Mocker
文件夹,并将Mocker.xcodeproj
拖放到您应用程序的Xcode项目的项目导航器中。它应该嵌套在您应用程序的蓝色项目图标下。它是在所有其他Xcode组之上还是之下无关紧要。
-
在项目导航器中选择
Mocker.xcodeproj
,并核实部署目标与您的应用程序目标相匹配。 -
接下来,在项目导航器中(蓝色项目图标)选择您的应用程序项目,以进入目标配置窗口,并在侧栏的“targets”标题下选择应用程序目标。
-
在窗口顶部的标签栏中,打开“General”面板。
-
在“Embedded Binaries”部分的
+
按钮上单击。 -
选择
Mocker.framework
。 -
这样就可以了!
Mocker.framework
将自动添加为目标依赖、链接框架和嵌入框架,在复制文件构建阶段完成,这是在模拟器和设备上构建所需的所有内容。
发布说明
查看CHANGELOG.md以获取变更列表。
许可协议
Mocker在MIT许可协议下提供。有关更多信息,请参阅LICENSE文件。