Подтвердить что ты не робот

Тест OCUnit для протоколов/callbacks/делегат в Objective-C

Используя OCUnit, есть ли способ проверить протоколы делегатов?

Я пытаюсь это сделать, что не работает.

-(void) testSomeObjDelegate {
  SomeObj obj = [[SomeObj alloc] initWithDelegate:self];
  [obj executeMethod];
}
-(void) someObjDelegateMethod {
  //test something here
}

Я попытаюсь вызвать метод obj в другом потоке и иметь тестовый сон до вызова делегата. Кажется, что должен быть более простой способ проверить это.

4b9b3361

Ответ 1

Тестирование делегата тривиально. Просто установите ivar в тесте в вашем методе обратного вызова и проверьте его после того, что должно инициировать обратный вызов делегата.

Например, если у меня есть класс Something, который использует делегат протокола SomethingDelegate и отправляет этот делегат -something:delegateInvoked: в ответ на какое-то сообщение, я могу проверить его likis:

@interface TestSomeBehavior : SenTestCase <SomethingDelegate>
{
    Something *_object;
    BOOL _callbackInvoked;
}
@end

@implementation TestSomeBehavior

- (void)setUp {
    [super setUp];
    _object = [[Something alloc] init];
    _object.delegate = self;
}

- (void)tearDown {
    _object.delegate = nil;
    [_object release];
    [super tearDown];
}

- (void)testSomeBehaviorCallingBack {
    [_object doSomethingThatShouldCallBack];

    STAssertTrue(_callbackInvoked,
                 @"Delegate should send -something:delegateInvoked:");
}

- (void)something:(Something *)something delegateInvoked:(BOOL)invoked {
    _callbackInvoked = YES;
}

@end

Я думаю, вы уже понимаете это, однако, из того, как вы сформулировали свой вопрос. (Я в основном размещаю это для других читателей.) Я думаю, вы действительно задаете более тонкий вопрос: как мне проверить что-то, что может появиться позже, например, что-то, что вращает runloop. Мой реплик - это ваше упоминание о снах и потоках.

Прежде всего, вы не должны просто произвольно вызывать метод в другом потоке. Вы должны делать это только в том случае, если это задокументировано, чтобы быть безопасным для использования таким образом. Причина в том, что вы не знаете, что делают внутренности класса. Например, он может планировать события в цикле выполнения, и в этом случае запуск метода в другом потоке заставит их произойти в другом цикле выполнения. Это приведет к зависанию внутреннего состояния класса.

Если вам требуется сделать, чтобы проверить что-то, что может занять некоторое время, вы можете сделать это, просто запустив текущий цикл цикла. Вот как я могу переписать отдельный метод тестирования выше, чтобы сделать это:

- (void)testSomeBehaviorCallingBack {
    NSDate *fiveSecondsFromNow = [NSDate dateWithTimeIntervalSinceNow:5.0];

    [_object doSomethingThatShouldCallBack];

    [[NSRunLoop currentRunLoop] runUntilDate:fiveSecondsFromNow];

    STAssertTrue(_callbackInvoked,
                 @"Delegate should send -something:delegateInvoked:");
}

Это запустит текущий цикл выполнения в режиме по умолчанию в течение 5 секунд, в предположении, что -doSomethingThatShouldCallBack будет планировать свою работу в цикле основного запуска в режиме по умолчанию. Обычно это нормально, потому что API-интерфейсы, которые работают таким образом, часто позволяют указать цикл запуска для использования, а также режим запуска. Если вы можете это сделать, вы можете использовать -[NSRunLoop runMode:beforeDate:] для запуска цикла выполнения только в этом режиме вместо этого, делая более вероятным, что работа, которую вы ожидаете сделать, будет.

Ответ 2

Пожалуйста, просмотрите Тестирование асинхронных сетевых атак. Я думаю, может вам помочь. Короче говоря, он делает это:

Добавьте следующий метод, который позаботится о синхронизации между кодом unit test и тестируемым асинхронным кодом:

- (BOOL)waitForCompletion:(NSTimeInterval)timeoutSecs {
    NSDate *timeoutDate = [NSDate dateWithTimeIntervalSinceNow:timeoutSecs];

    do {
        [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:timeoutDate];
        if([timeoutDate timeIntervalSinceNow] < 0.0)
            break;
    } while (!done);

    return done;
}