如何对NSURLConnection Delegate进行单元测试



如何对NSURLConnection委托进行单元测试?我制作了一个ConnectionDelegate类,它符合不同的协议,可以将来自web的数据提供给不同的ViewController。在我走得太远之前,我想开始写我的单元测试。但我不知道如何在没有互联网连接的情况下将它们作为一个单元进行测试。我还想知道应该如何处理异步回调。

这与Jon的回应类似,但无法将其放入评论中。第一步是确保您没有创建真正的连接。实现这一点的最简单方法是将连接的创建拉到工厂方法中,然后在测试中替换工厂方法。有了OCMock的部分mock支持,这可能看起来是这样的。

在你真正的课堂上:

- (NSURLConnection *)newAsynchronousRequest:(NSURLRequest *)request
{
    return [[NSURLConnection alloc] initWithRequest:request delegate:self];
}

在您的测试中:

id objectUnderTest = /* create your object */
id partialMock = [OCMockObject partialMockForObject:objectUnderTest];
NSURLConnection *dummyUrlConnection = [[NSURLConnection alloc] 
    initWithRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"file:foo"]] 
    delegate:nil startImmediately:NO];
[[[partialMock stub] andReturn:dummyUrlConnection] newAsynchronousRequest:[OCMArg any]];

现在,当被测试对象试图创建URL连接时,它实际上得到了在测试中创建的伪连接。伪连接不一定是有效的,因为我们没有启动它,它永远不会被使用。如果您的代码确实使用了连接,您可以返回另一个mock,一个模拟NSURLConnection的mock。

第二步是在您的对象上调用触发NSURLConnection创建的方法:

[objectUnderTest doRequest];

因为被测试的对象没有使用真正的连接,所以我们现在可以从测试中调用委托方法。对于NSURLResponse,我们使用另一个mock,响应数据是从测试中其他地方定义的字符串创建的:

int statusCode = 200;
id responseMock = [OCMockObject mockForClass:[NSHTTPURLResponse class]];
[[[responseMock stub] andReturnValue:OCMOCK_VALUE(statusCode)] statusCode];
[objectUnderTest connection:dummyUrlConnection didReceiveResponse:responseMock];
NSData *responseData = [RESPONSE_TEXT dataUsingEncoding:NSASCIIStringEncoding];
[objectUnderTest connection:dummyUrlConnection didReceiveData:responseData];
[objectUnderTest connectionDidFinishLoading:dummyUrlConnection];

就是这样。你已经有效地伪造了被测试对象与连接的所有交互,现在你可以检查它是否处于应该处于的状态。

如果您想查看一些"真实"代码,请查看CCMenu项目中使用NSURLConnection的类的测试。这有点令人困惑,因为测试的类也被命名为connection。

http://ccmenu.svn.sourceforge.net/viewvc/ccmenu/trunk/CCMenuTests/Classes/CCMConnectionTest.m?revision=129&view=markup

EDIT(2-18-2014):我刚刚偶然发现这篇文章有一个更优雅的解决方案。

http://www.infinite-loop.dk/blog/2011/04/unittesting-asynchronous-network-access/

本质上,您有以下方法:

- (BOOL)waitForCompletion:(NSTimeInterval)timeoutSecs {
    NSDate *timeoutDate = [NSDate dateWithTimeIntervalSinceNow:timeoutSecs];
    do {
        [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:timeoutDate];
        if([timeoutDate timeIntervalSinceNow] < 0.0)
            break;
    } while (!done);
    return done;
}

在你的测试方法结束时,你要确保事情没有超时:

STAssertTrue([self waitForCompletion:5.0], @"Timeout");

基本格式:

- (void)testAsync
{
    // 1. Call method which executes something asynchronously 
    [obj doAsyncOnSuccess:^(id result) {
        STAssertNotNil(result);
        done = YES;
    }
    onError:^(NSError *error) [
        STFail();
        done = YES;
    }
    // 2. Determine timeout
    STAssertTrue([self waitForCompletion:5.0], @"Timeout");
}    

===============

我参加聚会迟到了,但我遇到了一个非常简单的解决方案。(非常感谢http://www.cocoabuilder.com/archive/xcode/247124-asynchronous-unit-testing.html)

.h文件:

@property (nonatomic) BOOL isDone;

.m文件:

- (void)testAsynchronousMethod
{
    // 1. call method which executes something asynchronously.
    // 2. let the run loop do its thing and wait until self.isDone == YES
    self.isDone = NO;
    NSDate *untilDate;
    while (!self.isDone)
    {
        untilDate = [NSDate dateWithTimeIntervalSinceNow:1.0]
        [[NSRunLoop currentRunLoop] runUntilDate:untilDate];
        NSLog(@"Polling...");
    }
    // 3. test what you want to test
}

在异步方法正在执行的线程中,CCD_ 1被设置为CCD_。

因此,在本例中,我在步骤1创建并启动了NSURLConnection,并将其委托为这个测试类。在中

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response

我设置了self.isDone = YES;。我们打破while循环,执行测试。完成。

我避免在单元测试中联网。相反:

  • 我在一个方法中隔离NSURLConnection
  • 我创建了一个测试子类,覆盖该方法以删除NSURLConnection的所有痕迹
  • 我写了一个测试来确保有问题的方法在我需要的时候被调用。然后我知道它会在现实生活中引发NSURL连接

然后我集中讨论更有趣的部分:合成具有各种特性的模拟NSURLResponse,并将它们传递给NSURLConnectionDelegate方法。

我最喜欢的方法是将NSURLProtocol子类化,并让它响应所有http请求或其他协议。然后在-setup方法中注册测试协议,并在-tearDown方法中注销它。然后,您可以让这个测试协议将一些众所周知的数据返回到代码中,这样您就可以在单元测试中验证它。

我写了几篇关于这个主题的博客文章。与您的问题最相关的可能是使用NSURLProtocol注入测试数据和单元测试异步网络访问。

您可能还想看看我的ILCannedURLProtocol,它在前面的文章中有描述。源代码可在Github上获得。

您可能需要给它一个机会:

https://github.com/marianoabdala/ZRYAsyncTestCase

以下是正在进行单元测试的NSURLConnection的一些示例代码:https://github.com/marianoabdala/ZRYAsyncTestCase/blob/12a84c7f1af1a861f76c7825aef6d9d6c53fd1ca/SampleProject/SampleProjectTests/SampleProjectTests.m#L33-L56

以下是我所做的:

  1. 获取XAMPP控制面板http://www.apachefriends.org/en/xampp.html
  2. 启动apache服务器
  3. 在您的~/Sites文件夹中,放置一个测试文件(无论您想要什么数据,我们都将其称为my file.test
  4. 使用URL启动您的代理http://localhost/~用户名/myfile.test
  5. 在不使用apache服务器时停止它

最新更新