【问题标题】:Test that NSURLSessionConfiguration settings are obeyed测试是否遵守 NSURLSessionConfiguration 设置
【发布时间】:2015-12-16 03:35:18
【问题描述】:

我使用一些默认设置创建了一个 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

    ...

}

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

【问题讨论】:

    标签: ios nsurlsession nsurlprotocol nsurlsessionconfiguration


    【解决方案1】:

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

    我会质疑你在短语中使用“继承”这个词

    有没有办法检查我设置的那些参数是否被请求遵守和继承?

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

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

    通过在 NSURLSession 配置中注释掉包括 CustomProtocol 在内的行,并在有或没有这些行的情况下运行您的代码,我获得了一些潜在的有价值的信息:

    1. 通过注释掉包括 CustomProtocol 在内的行,实际上已发出请求(但失败),正如 Charles HTTP 代理所通知的那样。我还在您的方法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 授权标头,请确保服务器可以正确响应,并且如果您期望得到肯定的响应,则凭据是有效的
    • 记得注册您的 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 基本框架 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 类进行配置时,它可用于验证对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);
            }
        }];
    }
    

    【讨论】:

    • 似乎即使使用completionHandler 处理成功的请求,NSLog(@"Allowscellular: %@",allowsCellularString) 也会返回YES,即使会话配置中的setAllowsCellularAccess 为NO。这同样适用于其他参数组。非常感谢您的回答,但如果我可以检查配置上设置的这些参数是否被请求使用而无需手动测试,我仍然感到困惑。
    • 感谢您的反馈和澄清。我想尝试并提供更多帮助,但我不确定您想到的是哪种类型的解决方案。您已声明您希望“检查参数是否已设置……不必手动测试”。这是一个逻辑矛盾。这就像说“不尝就想知道味道如何”、“不打开就想知道里面是什么”、“不启动就想知道这辆旧车能不能跑”。您是否正在寻找其他方法(除了测试)来告知参数是否已设置?
    • 当我手动检查时,我的目的是创建单元测试来检查这些参数是否符合我的预期,这样我就不必手动检查了。
    • 好的。我明白。我可能会想出一些单元测试来验证参数是否正确。您是否可以发布与自定义NSURLProtocol 子类相关的代码,例如CustomProtocol 的实现(.m 文件)?
    • 嘿 Jason,代码非常类似于 Apple 的自定义协议示例@developer.apple.com/library/ios/samplecode/CustomHTTPProtocol/… 感谢您的时间,但请不要觉得有义务!我自己还在研究它,如果我想出一个好的方法来单元测试/确认这些参数,那么我也会给出答案
    猜你喜欢
    • 1970-01-01
    • 2016-11-30
    • 1970-01-01
    • 2019-12-15
    • 1970-01-01
    • 1970-01-01
    • 2011-07-26
    • 2014-09-05
    • 1970-01-01
    相关资源
    最近更新 更多