在XCTests上管理多个异步操作

时间:2015-08-31 10:20:54

标签: objective-c asynchronous xctest xctestexpectation

我面临着XCTestCase对象中异步操作的问题。 我从一个expectation-waitForExpectation链开始,有时将期望实例作为方法参数传递,以使其由异步操作完成块完成。 现在我改变了方法,因为我不能像以前那样看到糟糕的编写代码,并且我尝试了这种方式:

- (void)testThatItGoesToTheRightSport
{
  if (! appDelegateInstance.firstTimeLoaded)
  {
     [self __waitForLoadingAppAndDo:^(BOOL launched)
     {
       if (launched)
       {
          [self __goToLiveWithHandler:^(BOOL success) {
            if (success)
            {
              [self __goToLiveSport:[NSNumber numberWithUnsignedInteger:kDefaultSportCode]
                            handler:^(BOOL success) {
               if (! success)
               {
                 XCTFail(@"Test failed");
               }
             }];
           }
          }];
       }
     }];
  }
  else
  {
    [self __goToLiveWithHandler:^(BOOL success) {
      if (success)
      {
        [self __goToLiveSport:[NSNumber numberWithUnsignedInteger:kDefaultSportCode]
                      handler:^(BOOL success) {
          if (! success)
          {
            XCTFail(@"Test failed");
          }
        }];
      }
    }];
  }
}

__waitForLoadingAppAndDo:方法实现为

- (void)__waitForLoadingAppAndDo:(void (^)(BOOL launched))afterBlock
{
  if (! afterBlock)
      XCTFail(@"No afterBlock");

  XCTestExpectation *dataEx = [self expectationForNotification:NOTIFICATION_HOME_LOADED
                                                        object:nil
                                                       handler:^BOOL(NSNotification *notification) {
                                                           [dataEx fulfill];
                                                           return YES;
                                                       }];
  [self waitForExpectationsWithTimeout:SOCKOPT_TIMEOUT handler:^(NSError *error)
   {
       if (error)
       {
           XCTFail(@"No data received. %@", [error localizedDescription]);
       }
       else
       {
         dispatch_async(dispatch_get_main_queue(), ^{
           afterBlock(YES);
         });
       }
   }];
}

其他__方法与此类似。显然,现在,testThat方法不等待方法完成处理程序。如何改进它以及如何使testThat方法等待完成? XCTestExpectation是这样的吗? (告诉我不是eheh)

添加:那么,这是一种独特的方式吗?

- (void)testThatItGoesToTheRightSport
    {
       if (! appDelegateInstance.firstTimeLoaded)
       {
         XCTestExpectation *waitingLoading = [self expectationWithDescription:@"loadingApp"];
         [self waitForExpectationsWithTimeout:SOCKOPT_TIMEOUT handler:^(NSError *error) {
           if (error)
           {
             XCTFail(@"After block was not called.");
           }
         }];
         [self __waitForLoadingAppAndDo:^(BOOL launched)
         {
           if (launched)
           {
              [self __goToLiveWithHandler:^(BOOL success) {
                if (success)
                {
                  [waitingLoading fulfill];
                  [...]
                }
              }];
           }
          }];

添加-2

我试过

__block NSError *internalError = nil;
__block XCTestExpectation *finishedTest = [self expectationWithDescription:@"finishedTest"];
dispatch_group_t asyncGroup = dispatch_group_create();

if (! appDelegateInstance.firstTimeLoaded)
{
  dispatch_group_enter(asyncGroup);
  [self __waitForLoadingAppAndDo:^(BOOL launched) {
    if (! launched)
    {
      internalError = [TestUtilities notLoadedAppError];
    }
    dispatch_group_leave(asyncGroup);
  }];

  dispatch_group_enter(asyncGroup);
  [self __goToLiveWithHandler:^(BOOL success) {
    if (! success)
    {
      internalError = [NSError errorWithDomain:@"goLive"
                                          code:-1
                                      userInfo:@{NSLocalizedDescriptionKey : @"Errore apertura Live"}];
    }
    dispatch_group_leave(asyncGroup);
  }];

  dispatch_group_notify(asyncGroup, dispatch_get_main_queue(), ^{
    [finishedTest fulfill];
  });

但是这些组是异步调用的,无需等待完成块。 例如:我希望首先启动 OP1 ,然后在 OP1 完成块中启动 OP2

添加-3 : 我使用了dispatch_semaphore的不同方法。我喜欢它,但有一件事我会替换。如果我要等待信号,我阻止主线程,所以我必须dispatch_async wait命令如下:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
  dispatch_semaphore_wait(loadedApp, DISPATCH_TIME_FOREVER);
  dispatch_async(dispatch_get_main_queue(), ^{
    [self __goToLiveWithHandler:^(BOOL success) {
      if (success)
      {
        [...]
      }
    }];
  });
});

有没有办法避免这种情况?

1 个答案:

答案 0 :(得分:1)

使用

dispatch_group

创建一系列事件。

在测试开始时:

  

Dispatch_group_t group = dispatch_group_create();

在每次异步部分调用之前:

Dispatch_group_enter(group);

当每个异步部分完成使用时:

Dispatch_group_leave(group);

(“enter”的数量必须等于“leave”的数量)

在异步代码“wait”结束时:

// called when "group" |enter|=|leave|
Dispatch_group_apply(group,main_queue,^{

      // check your conditions....
       If (success) [expectation fulfill];
Else // failure
});

Expectation wait....// add the expectation wait handler as before

dispatch_group有不同的变体,因此您可能需要根据用例进行调整。

希望有所帮助

******* ******* EDIT

根据您的评论,您可能希望以不同方式嵌套组。例如:

// create the expectation
Expectation = ....

dispatch_group_t myGroup = dispatch_group_create();

dispatch_group_enter(myGroup);
// do first portion
dispatch_group_leave(myGroup);

// do the second portion after the first
dispatch_group_apply(myGroup,dispatch_queue..., ^{
   //second code portion

   // chain as many as needed using this technique

   // eventually you will need to fulfill your "expectation
   // ...
   if (success) {
       [expectation fullfil];
   } else {
       XCTAssert(fail,@"The test failed because ....");
   }
});

[self expectation wait...]