如何使用OCMock在块中测试异步方法

时间:2013-09-06 22:12:28

标签: objective-c-blocks ocmock

我似乎无法弄清楚如何测试这种方法:

- (void)writer:(id)writer didFailWithError:(NSError *)error;
{    
 [self.alertView dismissWithClickedButtonIndex:0 animated:YES];

  void (^alertViewBlock)(int) = ^(int buttonIndex)
  {
    if (buttonIndex == 1)
    {
        [self performSelectorOnMainThread:@selector(savePostponeReasonsAsynchronously) withObject:nil waitUntilDone:NO];
    }
    else
    {
        NSLog(@"dismissed");

        self.savePostponeReasonsQueue = nil;
    }
  };

 [self showPostponeReasonFailedAlert:alertViewBlock];
}

具体如何测试调用选择器savePostponeReasonsAsynchronously?

感谢

2 个答案:

答案 0 :(得分:3)

测试异步方法调用的一种方法是等待它们完成:

__block BOOL isRunning = YES;
[[[myPartialMock expect] andDo:^(NSInvocation *invocation){ isRunning = NO; }] savePostponeReasonsAsynchronously];
myPartialMock writer:nil didFailWithError:nil; 

NSDate *timeout = [NSDate dateWithTimeIntervalSinceNow:10];
do {
  [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
                           beforeDate:timeout];
} while (isRunning);

STAssertFalse(isRunning, @"Test timed out.");
[myPartialMock verify];

这是我通过观察Rob Napier的test code for RNCryptor学到的一种技术,它也有一些使用信号量的好技巧。

答案 1 :(得分:0)

我写了一个实现“承诺”的库。事实证明,这个概念不仅可用于解决复杂的异步问题,还可用于测试:

假设您有一些异步方法,并想检查是否将调用完成处理程序,以及它是否返回预期结果。作为奖励,您还需要设置测试人员等待完成处理程序的超时。如果它到期,测试应该失败。

通用完成处理程序块:

typedef void (^completion_block_t)(id result);

你的testie,一个可能基于循环运行的异步方法:

- (void) asyncFooWithCompletion:(completion_block_t)completionHandler;

您的测试可能如下所示:

- (void) testAsyncFooWithCompletion 
{
    // Your test - executing on the main thread.

    RXPromise* handlerPromise = [RXPromise new];

    [foo asyncFooWithCompletion:^(id result){
        // possibly perform assertions         
        ...

        if (result is expected) {
            // expected in this test scenario
            // resolve promise with a value indicating success:
            [handlerPromise fulfillWithValue:@"OK"];
        }
        else {
            // unexpected in this test scenario
            // resolve promise with an error:
            [handlerPromise rejectWithReason:@"Unexpected result"]; 
        }
    }];

注意:asyncFooWithCompletion:的工作负载可以在运行循环上进行调度,该运行循环在相应的线程上执行 - 例如主线。

完成处理程序也可以在同一个运行循环上执行,例如在主线上。

    // Set a timeout. If the timeout expires before the handler get called, 
    // the promise will be resolved with a "timeout error":
    [handlerPromise setTimeout:5.0];


    // Register handlers, which get called when _handlerPromise_ will be resolved.
    // We did perform a all assertions in the completion handler. Thus here, 
    // just catch unexpected failures - including a timeout:
    [handlerPromise.thenOn(dispatch_get_main_queue(), nil, 
    ^id(NSError* error) {
        // We are on the main thread
        ADD_FAILURE() << [[error description] UTF8String];
        return error;
     })  
     runLoopWait]; // wait on the current run loop.

     // if we reach here, assertions have been executed (on the main thread) 
     // and all handlers have been returned.

}    // end test