URLMock
URLMock 是一个用于模拟和存根 URL 请求和响应的 Objective-C 框架。它与基于 Foundation NSURL 加载系统的 API(例如 NSURLConnection、NSURLSession 和 AFNetworking)一起工作,几乎不需要修改您的代码。
特性
- 简单、文档丰富的 Objective-C API
- 必要的设置最小化
- 与基于 Foundation NSURL 加载系统的 API 一起工作
- 考虑到响应存根和单元测试设计
- 可用于项目部分或全部 URL 请求
- 经过充分测试,包括许多有用的测试工具
- 适用于 macOS、iOS 和 tvOS
URLMock 1.3.6 新增功能
URLMock 1.3.6 添加了对使用 Swift Package Manager 进行安装的支持。
安装
要开始使用 URLMock,最简单的方法是使用 CocoaPods 进行安装。
pod 'URLMock'
安装子规格
URLMock 有两个 CocoaPods 子规格,分别是 TestHelpers
和 SubclassResponsibility
。其中 TestHelpers
包含了一大类有用的测试函数。更多详情请参阅 UMKTestUtilities.h
。您可以通过向 Podfile 添加以下行进行安装
pod 'URLMock/TestHelpers'
同样,通过向 Podfile 添加以下行可以安装 SubclassResponsibility
子规格
pod 'URLMock/SubclassResponsibility'
此子规格向 NSException
添加方法,以便于为必须由子类提供实现的函数抛出异常。更多详情请参阅 NSException+UMKSubclassResponsibility.h。
使用 URLMock
URLMock 是同时考虑到响应模拟和单元测试而设计的。二者工作方式非常相似。
响应模拟
使用 URLMock 进行响应模拟非常简单。
首先,启用 URLMock。
[UMKMockURLProtocol enable];
如果您正在使用 NSURLSession
而不是共享会话,还需要将 UMKMockURLProtocol
添加到会话配置允许可用协议类集合中。
NSURLSessionConfiguration *configuration = …;
configuration.protocolClasses = @[ [UMKMockURLProtocol class] ];
NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration];
接下来,添加一个预期的模拟请求和响应。
// The request is a POST with some JSON data
NSURL *URL = [NSURL URLWithString:@"http://host.com/api/v1/person"];
id requestJSON = @{ @"person" : @{ @"name" : @"John Doe",
@"age" : @47 } };
id responseJSON = @{ @"person" : @{ @"id" : @1,
@"name" : @"John Doe",
@"age" : @47 } };
[UMKMockURLProtocol expectMockHTTPPostRequestWithURL:URL
requestJSON:requestJSON
responseStatusCode:200
responseJSON:responseJSON];
模拟请求和响应不限于 JSON 体内容;它们还可以有字符串体、WWW 表单编码参数字典或任意 NSData
实例的体。此外,还有用于返回错误或以延迟的方式返回数据块的模拟响应器,我们将在未来添加更多响应器。
当执行实际请求时,您将收到模拟响应。在使用 URLMock 时,您不需要更改代码。事情应该会如常进行。例如,以下 URLConnection 代码将接收上面的模拟响应
NSURL *URL = [NSURL URLWithString:@"http://host.com/api/v1/person"];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:URL];
request.HTTPMethod = @"POST";
id bodyJSON = @{ @"person" : @{ @"name" : @"John Doe", @"age" : @47 } };
request.HTTPBody = [NSJSONSerialization dataWithJSONObject:bodyJSON
options:0
error:NULL];
// Create the connection as usual
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:…];
以下 AFNetworking 代码可以达到相同的效果
NSURL *base = [NSURL URLWithString:@"http://host.com/api/v1/"];
id params = @{ @"person" : @{ @"name" : @"John Doe", @"age" : @47 } };
// Send a POST as usual
AFHTTPRequestOperationManager *om = [[AFHTTPRequestOperationManager alloc] initWithBaseURL:base];
om.requestSerializer = [AFJSONRequestSerializer serializer];
[om POST:@"person" parameters:params success:^(AFHTTPRequestOperation *op, id object) {
…
} failure:^(AFHTTPRequestOperation *op, NSError *error) {
…
}];
模式匹配模拟请求
您还可以使用UMKPatternMatchingMockRequest
创建一个对匹配的请求动态响应的模拟请求。要创建模式匹配模拟请求,您需要提供一个URL模式,例如@"http://hostname.com/:resource/:resourceID"
。当URL请求匹配此模式时,模拟请求将使用其响应器生成块生成适当的响应器。
NSString *pattern = @"http://hostname.com/accounts/:accountID/followers";
UMKPatternMatchingMockRequest *mockRequest = [[UMKPatternMatchingMockRequest alloc] initWithPattern:pattern];
mockRequest.HTTPMethods = [NSSet setWithObject:kUMKMockHTTPRequestPostMethod];
mockRequest.responderGenerationBlock = ^id<UMKMockURLResponder>(NSURLRequest *request, NSDictionary *parameters) {
NSDictionary *requestJSON = [request umk_JSONObjectFromHTTPBody];
// Respond with
// {
// "follower_id": «New follower’s ID»,
// "following_id": «Account ID that was POSTed to»
// }
UMKMockHTTPResponder *responder = [UMKMockHTTPResponder mockHTTPResponderWithStatusCode:200];
[responder setBodyWithJSONObject:@{ @"follower_id" : requestJSON[@"follower_id"],
@"following_id" : @([parameters[@"accountID"] integerValue]) }];
return responder;
};
[UMKMockURLProtocol addExpectedMockRequest:mockRequest];
有关更多信息,请参阅UMKPatternMatchingMockRequest
的文档。
单元测试
使用URLMock进行单元测试与响应存根非常相似,但您可以使用一些额外的API来简化单元测试。
首先,使用+setVerificationEnabled:
在UMKMockURLProtocol
中启用验证。这会使系统能够跟踪是否接收到了任何意外的请求。在您的xCASETest
的+setUp
方法中这样做是有意义的。您也可以在您的+tearDown
方法中禁用验证。
+ (void)setUp
{
[super setUp];
[UMKMockURLProtocol enable];
[UMKMockURLProtocol setVerificationEnabled:YES];
}
+ (void)tearDown
{
[UMKMockURLProtocol setVerificationEnabled:NO];
[UMKMockURLProtocol disable];
[super tearDown];
}
在每个测试之前(或之后),调用+[UMKMockURLProtocol reset]
。这将重置UMKMockURLProtocol
的期望回其原始状态。它不会更改是否启用了验证。
如果您正在使用XCTest,这是在测试用例的-setUp
(或-tearDown
)方法中执行的理想位置。
- (void)setUp
{
[super setUp];
[UMKMockURLProtocol reset];
}
在执行您正在测试的代码之后,向UMKMockURLProtocol
发送+verifyWithError:
信息。如果没有收到意外的模拟请求并处理了所有预期的模拟请求,则它将返回YES
。
NSError *error = nil;
XCTAssertTrue([UMKMockURLProtocol verifyWithError:&error], @"…");
为了最严格的测试,在您的UMKMockHTTPRequest
实例上启用标题检查。当启用时,模拟请求只匹配具有等效标题的URL请求。您可以通过将模拟HTTP请求的checksHeadersWhenMatching
属性设置为YES
或使用-initWithHTTPMethod:URL:checksHeadersWhenMatching:
来启用模拟HTTP请求的标题检查。
UMKMockHTTPRequest *request = [UMKMockHTTPRequest mockHTTPGetRequestWithURL:URL];
request.checksHeadersWhenMatching = YES;
请注意,一些网络API——尤其是AFNetworking——发送了您未显式设置的标题,因此在创建模拟请求之前,您应该确定这些标题是什么。为了使事情变得更简单,您可以使用+[UMKMockHTTPRequest setDefaultHeaders:]
为新的UMKMockHTTPRequest
实例设置默认标题。例如,如果您正在使用AFNetworking的默认HTTP请求序列化程序,您可以使用这种方式设置默认标题
[UMKMockHTTPRequest setDefaultHeaders:[[AFHTTPRequestSerializer serializer] HTTPRequestHeaders]];
非HTTP协议
默认情况下,URLMock仅支持HTTP和HTTPS。然而,它被设计为可以与任何NSURLSession支持的URL协议一起工作。如果你正在使用自定义的方案或URL协议,并希望添加对模拟请求和响应的支持,你只需创建符合UMKMockURLRequest
和UMKMockURLResponder
协议的类即可。查看UMKMockHTTPRequest
和UMKMockHTTPResponder
的实现示例。
所有者
@jnjosh, @prachigauriar, @macdrevx, 和 @dfowj 目前是URLMock的所有者。在问题或提交请求中提到我们,以便询问功能、项目方向或请求代码审查。
贡献、提交bug或请求增强功能
当前的URLMock非常可用,但仍有很多可以改进的地方。如果你想帮助我们修复bug或添加功能,请提交一个pull request!
我们使用GitHub问题跟踪器来处理bug、增强功能请求和有限的客户支持,因此请为这些内容之一创建一个问题。
通常,pull request应该在合并前至少得到2个项目所有者的代码审查和
许可证
所有代码均受MIT许可证的许可。您可以随意使用它。