代码之家  ›  专栏  ›  技术社区  ›  Luca D'Alberti

管理XCTests上的多个异步操作

  •  0
  • Luca D'Alberti  · 技术社区  · 11 年前

    我正面临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];
      });
    

    但这些组被称为异步组,无需等待完成块。 例如:我期待着 操作1 首先开始,在 操作1 完井区块 操作2 将启动。

    添加-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 回复  |  直到 11 年前
        1
  •  1
  •   Avba    11 年前

    使用

    dispatch_group
    

    创建一系列事件。

    测试开始时:

    Dispatch_group_t group = dispatch_group_create();

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

    Dispatch_group_enter(group);
    

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

    Dispatch_group_leave(group);
    

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

    在异步代码“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...]
    
    推荐文章