2014-02-04 6 views
0

(я могу использовать это в совершенно неправильном порядке, так что не стесняйтесь, чтобы бросить вызов помещения этого поста.)Unit-тестирование простого использования RACSignal с RACSubject

У меня есть небольшой RACTest app (sound familiar?), что Я пытаюсь выполнить тестирование. Я бы хотел протестировать MPSTicker, один из самых активных компонентов ReactiveCocoa. Он имеет сигнал, который отправляет значение один раз в секунду, которое накапливается, если флаг накопления установлен в YES. I added an initializer, чтобы принять пользовательский сигнал для его возрастающего сигнала, а не только на основе таймера.

Я хотел модульное тестирование пары поведения MPSTicker:

  • Убедитесь, что его приращение накопления сигнала надлежащим образом (т.е. монотонно возрастает), когда накопление включено и входной сигнал приращения посылает новое значение.
  • Убедитесь, что он отправляет то же значение (а не приращение), когда входной сигнал отправляет значение.

Я добавил a test that uses the built-in timer to test the first increment, и она работает, как я ожидал (хотя я ищу рекомендации по улучшению кретиничной инициализации RACSequence я сделал, чтобы получить сигнал со значением @(1) я хотел.)

I У меня было очень трудное время выяснить, какой входной сигнал я могу предоставить MPSTicker, который я могу вручную отправить значения. Я представляя испытание, как:

<set up ticker> 
<send a tick value> 
<verify accumulated value is 1> 
<send another value> 
<verify accumulated value is 2> 

Я попытался с помощью RACSubject так что я могу использовать sendNext: запихнуть в ценностях, как я считаю нужным, но это не работает, как я ожидал. Вот два сломанных испытания:

- (void)testManualTimerTheFirst 
{ 
    // Create a custom tick with one value to send. 
    RACSubject *controlledSignal = [RACSubject subject]; 
    MPSTicker *ticker = [[MPSTicker alloc] initWithTickSource:controlledSignal]; 
    [ticker.accumulateSignal subscribeNext:^(id x) { 
     NSLog(@"%s value is %@", __func__, x); 
    }]; 

    [controlledSignal sendNext:@(2)]; 
} 

- (void)testManualTimerTheSecond 
{ 
    // Create a custom tick with one value to send. 
    RACSubject *controlledSignal = [RACSubject subject]; 
    MPSTicker *ticker = [[MPSTicker alloc] initWithTickSource:controlledSignal]; 

    BOOL success = NO; 
    NSError *error = nil; 
    id value = [ticker.accumulateSignal asynchronousFirstOrDefault:nil success:&success error:&error]; 

    if (!success) { 
     XCTAssertTrue(success, @"Signal failed to return a value. Error: %@", error); 
    } else { 
     XCTAssertNotNil(value, @"Signal returned a nil value."); 
     XCTAssertEqualObjects(@(1), value, @"Signal returned an unexpected value."); 
    } 

    // Send a value. 
    [controlledSignal sendNext:@(1)]; 
} 

В testManualTimerTheFirst, я никогда не видел никакой ценности из controlledSignal «s sendNext: прийти до моего subscribeNext: блока.

В testManualTimerTheSecond, я попытался с помощью asynchronousFirstOrDefault: вызова, чтобы получить первое значение из сигнала, затем посылается вручную значение по моей теме, но стоимость не придет через, и тест не пройден, когда asynchronousFirstOrDefault: истекло.

Что мне здесь не хватает?

ответ

0

Смотрите этот вопрос: https://stackoverflow.com/a/19127547/420594

XCAsyncTestCase имеет некоторые дополнительные функциональные возможности, чтобы для асинхронных тестов.

Кроме того, я еще не посмотрел на него, но мог бы ReactiveCocoaTests вас заинтересовал? С первого взгляда они, кажется, используют Expecta.

6

Это может не отвечать на ваш вопрос в точности, но это может дать вам представление о том, как эффективно протестировать ваши сигналы. Я использовал 2 подходы я до сих пор:

XCTestCase и TRVSMonitor

TRVSMonitor это небольшая утилита, которая приостанавливает текущий поток для вас, пока вы бежите ваши утверждения.Например:

TRVSMonitor *monitor = [TRVSMonitor monitor]; 

[[[self.service searchPodcastsWithTerm:@"security now"] collect] subscribeNext:^(NSArray *results) { 
    XCTAssertTrue([results count] > 0, @"Results count should be > 0"; 
    [monitor signal]; 

} error:^(NSError *error) { 
    XCTFail(@"%@", error); 
    [monitor signal]; 
}]; 

[monitor wait]; 

Как вы можете видеть, я говорю монитор ждать сразу после того, подписаться и сигнализировать его прекратить ожидание в конце subscribeNext и ошибок блоков, чтобы сделать его продолжать выполнение (так и другие тесты может работать тоже). Этот подход имеет преимущество не полагаться на статический тайм-аут, поэтому ваш код может работать до тех пор, пока ему это нужно.

Используя CocoaPods, вы можете легко добавить TRVSMonitor к вашему проекту:

pod "TRVSMonitor", "~> 0.0.3" 

Specta & Expecta

Specta является BDD/TDD (поведение приводом/Test Driven) рамки испытаний. Expecta - это структура, которая обеспечивает более удобные совпадения для утверждения. Он имеет встроенную поддержку асинхронных тестов. Это позволяет создавать более наглядные тесты с ReactiveCocoa, например, так:

it(@"should return a valid image, with cache state 'new'", ^AsyncBlock { 
    [[cache imageForURL:[NSURL URLWithString:SECURITY_NOW_ARTWORK_URL]] subscribeNext:^(UIImage *image) { 
     expect(image).notTo.beNil(); 
     expect(image.cacheState).to.equal(JPImageCacheStateNew); 

    } error:^(NSError *error) { 
     XCTFail(@"%@", error); 

    } completed:^{ 
     done(); 
    }]; 
}); 

Обратите внимание на использование ^AsyncBlock {. Использование просто ^{ подразумевало бы синхронный тест.

Здесь вы вызываете функцию done(), чтобы сигнализировать о завершении асинхронного теста. Я считаю, что Specta использует внутренний тайм-аут 10 секунд.

Используя CocoaPods, вы можете легко добавить Expecta & Specta:

pod "Expecta", "~> 0.2.3" 
pod "Specta", "~> 0.2.1" 
Смежные вопросы