0

我遇到了 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路吗?(告诉我不要呃)

补充:那么,这是独特的方式吗?

- (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将启动。

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

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

4

1 回答 1

1

利用

dispatch_group

创建一个事件链。

在测试开始时:

Dispatch_group_t group = dispatch_group_create();

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

Dispatch_group_enter(group);

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

Dispatch_group_leave(group);

(“进入”的次数必须等于“离开”的次数)

在异步代码“等待”的末尾:

// 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...]
于 2015-08-31T19:26:09.617 回答