测试是否遵守 NSURLSessionConfiguration 设置

Test that NSURLSessionConfiguration settings are obeyed

我使用一些默认设置创建了一个 NSURLSessionConfiguration,但是当我在我的自定义 NSURLProtocol 中看到使用该配置创建的请求对象时,似乎并没有继承所有这些设置,我有点困惑。

NSURLSessionConfiguration* config = [NSURLSessionConfiguration defaultSessionConfiguration];

NSMutableArray *protocolsArray = [NSMutableArray arrayWithArray:config.protocolClasses];

[protocolsArray insertObject:[CustomProtocol class] atIndex:0];

config.protocolClasses = protocolsArray;

// ex. set some random parameters

[config setHTTPAdditionalHeaders:@{@"Authorization":@"1234"}];
[config setAllowsCellularAccess:NO];
[config setRequestCachePolicy:NSURLRequestReturnCacheDataElseLoad];
[config setHTTPShouldSetCookies:NO];
[config setNetworkServiceType:NSURLNetworkServiceTypeVoice];
[config setTimeoutIntervalForRequest:4321];

// Create a request with this configuration and start a task

NSURLSession* session = [NSURLSession sessionWithConfiguration:config];

NSURLRequest* request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"https://google.com"]];

NSURLSessionDataTask* task = [session dataTaskWithRequest:request];

[task resume];

在我注册的自定义 NSURLProtocol 中

- (void)startLoading {
    ...

    // po [self.request valueForHTTPHeaderField:@"Authorization"] returns 1234
    //
    // However, I'm very confused why
    //
    //  - allowsCellularAccess
    //  - cachePolicy
    //  - HTTPShouldHandleCookies
    //  - networkServiceType
    //  - timeoutInterval
    //
    // for the request return the default values unlike for the header

    ...

}

有什么方法可以检查我设置的那些参数是否被请求遵守和继承?

在处理 http 请求时,从基础开始是有帮助的,例如 OS 是否实际发出了请求,是否收到了响应?这将在一定程度上帮助回答您关于检查请求是否确实遵守设置参数的问题。

我会质疑你在短语

中使用 "inherit" 这个词

Is there some way to check that those parameters I've set are obeyed and inherited by the request?

Object 面向编程中的继承具有非常特殊的含义。您实际上是否创建了具有特定属性的 NSURLRequest 的自定义 subclass(我们称之为 SubClassA),然后是另一个 subclass(我们称之为 SubClassB),并期待第二个 subclass (SubClassB) 从它的 parent (SubClassA) 继承属性?如果是这样,你提供的代码中肯定没有说明这一点。

有几个可用的 HTTP 代理程序可以帮助确认是否正在发送 HTTP 请求,如果收到响应,还可以让您检查请求和响应的详细信息。 Charles HTTP Proxy 就是这样一个程序。使用 Charles,我能够确定您提供的代码没有发出任何 HTTP 请求。因此,如果没有发出请求,您将无法确认或拒绝任何参数。

通过注释掉作为 NSURLSession 配置的一部分的 CustomProtocol 行,以及 运行 你的代码有或没有这些行,我获得了一些潜在的有价值的信息:

  1. 根据 Charles HTTP 代理的通知,通过注释掉包括 CustomProtocol 在内的行,实际上已发出请求(但失败了)。我还在你的方法中添加了一个完成块 dataTaskWithRequest。当 CustomProtocol 配置行被注释掉时,这个完成块 被命中 。 CustomProtocol 的 startLoading 方法未命中。
  2. 当离开原来的行使用 CustomProtocol 配置 NSURLSession 时,Charles HTTP 代理没有记录请求,完成处理程序是未命中。然而,CustomProtocol 的 startLoading 方法被命中。

请参阅下面的代码(对原始问题中发布的代码进行了修改)。

NSURLSessionConfiguration* config = [NSURLSessionConfiguration defaultSessionConfiguration];

NSMutableArray *protocolsArray = [NSMutableArray arrayWithArray:config.protocolClasses];

//[protocolsArray insertObject:[CustomProtocol class] atIndex:0];

//config.protocolClasses = protocolsArray;

// ex. set some random parameters

[config setHTTPAdditionalHeaders:@{@"Authorization":@"1234"}];
[config setAllowsCellularAccess:NO];
[config setRequestCachePolicy:NSURLRequestReturnCacheDataElseLoad];
[config setHTTPShouldSetCookies:NO];
[config setNetworkServiceType:NSURLNetworkServiceTypeVoice];
[config setTimeoutIntervalForRequest:4321];

// Create a request with this configuration and start a task

NSURLSession* session = [NSURLSession sessionWithConfiguration:config];

NSURLRequest* request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"https://google.com"]];

NSURLSessionDataTask* task = [session dataTaskWithRequest:request
                                        completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
                                            NSString * auth = [request valueForHTTPHeaderField:@"Authorization"];
                                            NSLog(@"Authorization: %@", auth);
                                            BOOL allowsCellular = [request allowsCellularAccess];
                                            NSString * allowsCellularString = allowsCellular ? @"YES" : @"NO";
                                            NSLog(@"Allows cellular: %@", allowsCellularString);
}];

[task resume];

这向您提供了 CustomProtocol 未正确处理请求的信息。是的,当 CustomProtocol 配置为 NSURLSession 的一部分时,startLoading 方法内的断点被命中,但这并不是 CustomProtocol 正确处理请求的最终证据。如 Apple (Protocol Support, NSURLProtocol Class Reference) 所述,使用 CustomProtocol 需要执行许多步骤,您应该确认您正在执行这些步骤。

一些要确保有效的事情:

  • 如果您使用的是 CustomProtocol,这意味着您可能正在尝试处理不同于 http、https、ftp、ftps 等的其他协议。
  • 确保您的端点(正在侦听 http 请求和响应的服务器)实际上可以接受请求和回复。
  • 如果您正在设置 HTTP 授权 Header,请确保服务器可以正确响应,并且如果您期望得到肯定响应,请确保凭据有效
  • 记得注册你的 CustomProtocol

例如:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.
    [NSURLProtocol registerClass:[CustomProtocol class]];
    return YES;
}

下面是一个单元测试,用于验证 NSURLSession 是否按预期运行(未明确使用我们的自定义协议)。请注意,当添加到 Apple 自己的 CustomHTTPProtocol 项目示例代码中时,此单元测试确实通过了,但使用我们的 very bare bones CustomProtocol

时未通过
- (void)testNSURLSession {
    XCTestExpectation *expectation = [self expectationWithDescription:@"Testing standard NSURL Session"];

    [[[NSURLSession sharedSession] dataTaskWithURL:[NSURL URLWithString:@"https://www.apple.com/"] completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
#pragma unused(data)
        XCTAssertNil(error, @"NSURLSession test failed with error: %@", error);
        if (error == nil) {
            NSLog(@"success:%zd / %@", (ssize_t) [(NSHTTPURLResponse *) response statusCode], [response URL]);
            [expectation fulfill];
        }
    }] resume];

    [self waitForExpectationsWithTimeout:3.0 handler:^(NSError * _Nullable error) {
        if(nil != error) {
            XCTFail(@"NSURLSession test failed with error: %@", error);
        }
    }];
}

下面是一个单元测试,可用于验证在使用我们自己的 CustomProtocol class 配置时对 NSURLSession 所做的配置是否符合预期。再次请注意,此测试使用 CustomProtocol 的空实现失败,但如果使用测试驱动开发(首先创建测试,然后创建允许测试通过的代码),这是预期的。

- (void)testCustomProtocol {
    XCTestExpectation *expectation = [self expectationWithDescription:@"Testing Custom Protocol"];

    NSURLSessionConfiguration* config = [NSURLSessionConfiguration defaultSessionConfiguration];
    NSMutableArray *protocolsArray = [NSMutableArray arrayWithArray:config.protocolClasses];

    [protocolsArray insertObject:[CustomProtocol class] atIndex:0];

    config.protocolClasses = protocolsArray;

    // ex. set some random parameters

    [config setHTTPAdditionalHeaders:@{@"Authorization":@"1234"}];
    [config setAllowsCellularAccess:NO];
    [config setRequestCachePolicy:NSURLRequestReturnCacheDataElseLoad];
    [config setHTTPShouldSetCookies:NO];
    [config setNetworkServiceType:NSURLNetworkServiceTypeVoice];
    [config setTimeoutIntervalForRequest:4321];

    // Create a request with this configuration and start a task
    NSURLSession* session = [NSURLSession sessionWithConfiguration:config];
    NSURLRequest* request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"https://www.apple.com"]];

    NSURLSessionDataTask* task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
#pragma unused(data)
        XCTAssertNil(error, @"test failed: %@", error.description);
        if (error == nil) {
            NSLog(@"success:%zd / %@", (ssize_t) [(NSHTTPURLResponse *) response statusCode], [response URL]);
            NSString * auth = [request valueForHTTPHeaderField:@"Authorization"];
            NSLog(@"Authorization: %@", auth);
            XCTAssertNotNil(auth);

            BOOL allowsCellular = [request allowsCellularAccess];
            XCTAssertTrue(allowsCellular);

            XCTAssertEqual([request cachePolicy], NSURLRequestReturnCacheDataElseLoad);

            BOOL shouldSetCookies = [request HTTPShouldHandleCookies];
            XCTAssertTrue(shouldSetCookies);

            XCTAssertEqual([request networkServiceType], NSURLNetworkServiceTypeVoice);

            NSTimeInterval timeOutInterval = [request timeoutInterval];
            XCTAssertEqualWithAccuracy(timeOutInterval, 4321, 0.01);
            [expectation fulfill];

        }

    }];

    [task resume];

    [self waitForExpectationsWithTimeout:3.0 handler:^(NSError * _Nullable error) {
        if(nil != error) {
            XCTFail(@"Custom Protocol test failed with error: %@", error);
        }
    }];
}