(我可能会以完全错误的方式使用它,所以请随意挑战这篇文章的前提。)
我有一个小RACTest app(sound familiar?),我正在尝试进行单元测试。我想测试MPSTicker,它是最基于ReactiveCocoa的组件之一。如果累积标志设置为YES,它有一个每秒发送一次累积值的信号。我added an initializer为其递增信号采取自定义信号,而不是仅基于定时器。
我想对MPSTicker的几个行为进行单元测试:
我添加了a test that uses the built-in timer to test the first increment,它按照我的预期工作(虽然我正在寻求改进愚蠢的RACSequence初始化的建议,但我得到的信号是我想要的@(1)
值。)
我很难确定哪些输入信号可以提供给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
sendNext:
subscribeNext:
阻止任何价值进入testManualTimerTheSecond
阻止。
在asynchronousFirstOrDefault:
中,我尝试使用asynchronousFirstOrDefault:
调用从信号中获取第一个值,然后手动向我的主题发送一个值,但该值未通过,并且测试{{1}}超时时失败。
我在这里缺少什么?
答案 0 :(得分:6)
这可能无法准确回答您的问题,但它可能会为您提供有关如何有效测试信号的见解。到目前为止,我自己使用了两种方法:
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&amp; 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&amp; Specta:
pod "Expecta", "~> 0.2.3"
pod "Specta", "~> 0.2.1"
答案 1 :(得分:0)
请参阅此问题:https://stackoverflow.com/a/19127547/420594
XCAsyncTestCase有一些额外的功能可以允许异步测试用例。
另外,我还没有深入研究过,但ReactiveCocoaTests对你有什么兴趣?乍一看,他们似乎正在使用Expecta。