管理 XCTest 上的多个异步操作

Manage multiple asynchronous operation on XCTests

我在 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 是这样吗? (告诉我不要嗯)

ADDING: 那么,这是唯一的方式吗?

- (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];
                  [...]
                }
              }];
           }
          }];

ADDING-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)
      {
        [...]
      }
    }];
  });
});

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

使用

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 有不同的变体,因此您可能需要根据您的用例进行调整。

希望对您有所帮助

*******编辑*******

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

// 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...]