iOS TestCase包含NS Timer的方法

时间:2015-12-05 14:43:51

标签: ios objective-c unit-testing xctest

我的视图控制器中有一个IBAction,看起来像这样

-(IBAction)signUpAction:(id)sender
 {
     AppDelegate *appDel = [[UIApplication sharedApplication]delegate];

    //check for internet Connection
    if(appDel.isReachable)
    {
        //Internet Connection available
        //perform animation od buttons and imagie view
        [self fallDownAnimation];

        //after animation perform model segue to corresponding view controller

        NSTimer* timer =   [NSTimer scheduledTimerWithTimeInterval:0.8f target:self selector:@selector(performRegistrationPageSegue) userInfo:nil repeats:NO];
        [[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
    }
    else
    {
        //No internet Connection
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:ALERT_VIEW_TITLE message:@"No Internet Connection" delegate:self cancelButtonTitle:nil otherButtonTitles:@"Okay", nil];
        [alert show];
    }
}



-(void)performRegistrationPageSegue{
    [self performSegueWithIdentifier:@"registerVCSegue" sender:self];
}

我想在signUpAction方法上编写一个测试用例,并验证是否执行了Segue。由于它有一个计时器,我写的测试用例失败了。 我需要一种方法来测试以下条件

My Current Testcase方法

-(void)testRegisterViewControllerSegueOnAvailableInternetConnection{
    AppDelegate *appDel = [[UIApplication sharedApplication]delegate];
    appDel.isReachable = YES;
    id loginMock = [OCMockObject partialMockForObject:_initialViewControllerToTest];

    [[loginMock expect] performSegueWithIdentifier:@"registerVCSegue" sender:[OCMArg any]];

    [loginMock performSelectorOnMainThread:@selector(signUpAction:) withObject:_initialViewControllerToTest.signUpButton waitUntilDone:YES];

    XCTAssert([loginMock verify],@"Segue to Register Page not Performed on Sign Up Click");
}

1 个答案:

答案 0 :(得分:1)

您需要输入一段时间的事件循环,以便可以处理计时器事件。如果不这样做,基本上不可能完全回归测试代码。这是一个简化的方法:

// Wait inside the event loop for a period of time indicated in seconds

+ (void) waitFor:(NSTimeInterval)maxWaitTime
{
  int numSeconds = (int) round(maxWaitTime);
  if (numSeconds < 1) {
    numSeconds = 1;
  }

  for ( ; numSeconds > 0 ; numSeconds--) @autoreleasepool {
    const int maxMS = 1000;
    const int incrMS = 1;
    const double seconds = 1.0 / (maxMS / incrMS);

    for (int ms = 0 ; ms < maxMS; ms += incrMS) @autoreleasepool {
      // One pass through the run loop for each time interval
      NSDate *maxDate = [NSDate dateWithTimeIntervalSinceNow:seconds];
      [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:maxDate];
    }
  }

  return;
}

带有选择器的更复杂的impl,可以在测试条件为真时调用它来返回:

+ (BOOL) waitUntilTrue:(id)object
              selector:(SEL)selector
           maxWaitTime:(NSTimeInterval)maxWaitTime
{
  NSAssert(object, @"object is nil");
  NSAssert(selector, @"selector is nil");
  NSMethodSignature *aSignature = [[object class] instanceMethodSignatureForSelector:selector];
  NSInvocation *anInvocation = [NSInvocation invocationWithMethodSignature:aSignature];
  [anInvocation setSelector:selector];
  [anInvocation setTarget:object];

  // Invoke test condition method once before the timing loop is entered, so that the
  // event loop will not be entered if the condition is initially TRUE.

  BOOL state;

  [anInvocation invoke];
  [anInvocation getReturnValue:&state];

  if (state) {
    return TRUE;
  }

  // The condition is FALSE, so enter the event loop and wait for 1 second
  // each iteration through the loop. The logic below makes sure that the
  // 1 second wait will be done at least once, even if wait time is less
  // than a full second.

  int numSeconds = (int) round(maxWaitTime);
  if (numSeconds < 1) {
    numSeconds = 1;
  }

  for ( ; numSeconds > 0 ; numSeconds--) @autoreleasepool {
    NSDate *maxDate = [NSDate dateWithTimeIntervalSinceNow:1.0];
    [[NSRunLoop currentRunLoop] runUntilDate:maxDate];

    [anInvocation invoke];
    [anInvocation getReturnValue:&state];

    if (state) {
      return TRUE;
    }    
  }

  return FALSE;
}