(我可能会以完全错误的方式使用它,所以请随意挑战这篇文章的前提.)
我有一个小的RACTest应用程序(听起来很熟悉吗?),我正在尝试进行单元测试.我想测试MPSTicker,它是最基于ReactiveCocoa的组件之一.如果累积标志设置为YES,它有一个每秒发送一次累积值的信号.我添加了一个初始化器来为其递增信号采用自定义信号,而不是仅基于定时器.
我想对MPSTicker的几个行为进行单元测试:
当累加使能且输入递增信号发送新值时,验证其累加信号是否正确递增(即单调增加).
当输入信号发送值时,验证它是否发送相同的值(而不是递增的值).
我添加了一个使用内置计时器来测试第一个增量的测试,并且它按照我的预期工作(尽管我正在寻求改进愚蠢的RACSequence初始化的建议,我做了以获得具有@(1)
我想要的值的信号. )
我很难确定我可以向MPSTicker提供哪些输入信号,我可以手动发送值.我想象一个测试:
我尝试使用一个,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
的sendNext:
通过来我subscribeNext:
挡.
在testManualTimerTheSecond
,我尝试使用该asynchronousFirstOrDefault:
调用从信号中获取第一个值,然后在我的主题上手动发送一个值,但该值未通过,并且测试在asynchronousFirstOrDefault:
超时时失败.
我在这里错过了什么?
这可能无法准确回答您的问题,但它可能会为您提供有关如何有效测试信号的见解.到目前为止,我自己使用了两种方法:
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(行为驱动/测试驱动)测试框架.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"