Objective C - 单元测试dispatch_async块?

时间:2012-09-17 16:45:23

标签: objective-c unit-testing dispatch-async

我读了其他帖子,提出了这个问题的解决方案。但是,他们的解决方案需要将hacky代码添加到我的应用程序中才能进行测试。对我来说,清洁代码比单元测试更重要。

我经常在我的应用程序中使用dispatch_async,我在单元测试时遇到了麻烦。问题是我的测试完成后块执行,因为它在主队列上异步运行。有没有办法等到块执行后再继续测试。

我不想仅仅因为单元测试而将完成传递给块

- (viod)viewDidLoad
{
   [super viewDidLoad];

   // Test passes on this
   [self.serviceClient fetchDataForUserId:self.userId];


   // Test fails on this because it's asynchronous
   dispatch_async(dispatch_get_main_queue(), ^{
      [self.serviceClient fetchDataForUserId:self.userId];
   });
}

- (void)testShouldFetchUserDataUsingCorrectId
{
   static NSString *userId = @"sdfsdfsdfsdf";
   self.viewController.userId = userId;
   self.viewController.serviceClient = [[OCMockObject niceMockForClass:[ServiceClient class]];

   [[(OCMockObject *)self.viewController.serviceClient expect] fetchDataForUserId:userId];
   [self.viewController view]; 
   [(OCMockObject *)self.viewController.serviceClient verify];
}

3 个答案:

答案 0 :(得分:39)

短暂运行主循环让它调用异步块:

- (void)testShouldFetchUserDataUsingCorrectId {
   static NSString *userId = @"sdfsdfsdfsdf";
   self.viewController.userId = userId;
   self.viewController.serviceClient = [[OCMockObject niceMockForClass:[ServiceClient class]];

   [[(OCMockObject *)self.viewController.serviceClient expect] fetchDataForUserId:userId];
   [self.viewController view];
   [[NSRunLoop mainRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.01]];
   [(OCMockObject *)self.viewController.serviceClient verify];
}

我认为这可能会在负载很重的系统上失败,或者如果你在主线程上有一堆其他东西(定时器或其他块)。如果是这种情况,则需要更长时间运行运行循环(这会减慢测试用例),或者反复运行它直到满足模拟对象的期望或达到超时(这需要向模拟对象添加方法)询问是否满足了它的期望。)

答案 1 :(得分:10)

将执行包装到dispatch_group,然后等待组通过dispatch_group_wait()完成所有已发送的块的执行。

答案 2 :(得分:0)

使用类似的方法签名制作一个dispatch_async包装器,然后调用真实的dispatch_async。依赖关系将包装器注入生产类并使用它。

然后创建一个模拟包装器,它记录排队的块并有一个额外的方法来同步运行所有排队的块。也许执行递归"阻塞"如果正在执行的块依次排列更多的块。

在单元测试中,将模拟包装器注入被测系统。然后,即使SUT认为它正在进行异步工作,您也可以使所有事情同步发生。

dispatch_group听起来也是一个很好的解决方案,但需要你的生产课程才能知道"在它排队的块的末尾ping调度组。