如何unit testing一个NSURLConnection委托?

我怎样才能unit testing我的NSURLConnection委托? 我做了一个ConnectionDelegate类,它符合不同的协议,以便将来自Web的数据提供给不同的ViewController。 在我太远之前,我想开始写我的unit testing。 但是我不知道如何testing他们作为一个单位没有互联网连接。 我也想我应该做什么来处理asynchronouscallback。

虽然这与Jon的回应类似,但不能将其纳入评论。 第一步是确保你没有创build一个真正的连接。 实现这个最简单的方法是将连接的创build拉入工厂方法,然后在testing中replace工厂方法。 与OCMock的部分模拟支持,这可能看起来像这样。

在你真实的课堂上:

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

在你的testing中:

 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]]; 

现在,当你的对象试图创buildURL连接时,它实际上会获得在testing中创build的虚拟连接。 虚拟连接不一定是有效的,因为我们没有启动它,它永远不会被使用。 如果你的代码使用连接,你可以返回另一个模拟,一个嘲笑NSURLConnection。

第二步是调用触发创buildNSURLConnection的对象的方法:

 [objectUnderTest doRequest]; 

由于被测对象没有使用真正的连接,我们现在可以从testing中调用委托方法。 对于NSURLResponse,我们使用另一个模拟,响应数据是从testing中其他地方定义的string创build的:

 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]; 

而已。 您已经有效地伪造了testing对象与连接之间的所有交互,现在可以检查它是否处于应该处于的状态。

如果您想查看一些“真实”代码,请查看使用NSURLConnections的CCMenu项目的类的testing。 这有点令人困惑,因为被testing的类也被命名为连接。

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

编辑(2-18-2014):我只是用一个更优雅的解决scheme偶然发现了这篇文章。

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; } 

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

 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"); } 

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

我迟到了,但我遇到了一个非常简单的解决scheme。 (非常感谢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 } 

isDone在asynchronous方法执行的线程中设置为YES

所以在这种情况下,我在步骤1创build并启动了NSURLConnection,并将其委托给此testing类。 在

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

我设置了self.isDone = YES; 。 我们跳出while循环并执行testing。 完成。

我在unit testing中避免了联网。 代替:

  • 我在一个方法中分离NSURLConnection。
  • 我创build一个testing子类,重写该方法来删除NSURLConnection的所有痕迹。
  • 我写一个testing,以确保有问题的方法将被调用时,我想。 那么我知道它会在现实生活中触发NSURLConnection。

然后我将重点放在更有趣的部分:合成具有各种特性的模拟NSURLResponses,并将它们传递给NSURLConnectionDelegate方法。

我最喜欢这样做的方式是NSURLProtocol的子类,并响应所有HTTP请求或其他协议。 然后在-setup方法中注册testing协议,并将其注销到-tearDown方法中。 然后,您可以使用此testing协议将一些众所周知的数据提供回您的代码,以便您可以在unit testing中对其进行validation。

我已经写了一些关于这个主题的博客文章。 与您的问题最相关的可能是使用NSURLProtocol注入testing数据和unit testingasynchronousnetworking访问 。

您可能还想看看我在前面的文章中描述的ILCannedURLProtocol。 源在Github上可用。

你可能想给这个机会:

https://github.com/marianoabdala/ZRYAsyncTestCase

这是一个NSURLConnectionunit testing的示例代码: 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文件夹中,放一个testing文件(无论你想要什么数据,我们都会把它称为my file.test )。
  4. 使用URL http://localhost/~username/myfile.test启动您的委托
  5. 停止使用Apache服务器时,不使用它。