使用 RACSubject 对 RACSignal 的简单用法进行单元测试 [英] Unit-testing a simple usage of RACSignal with RACSubject

查看:30
本文介绍了使用 RACSubject 对 RACSignal 的简单用法进行单元测试的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

(我可能以完全错误的方式使用它,所以请随意挑战这篇文章的前提.)

(I may be using this in a totally incorrect manner, so feel free to challenge the premise of this post.)

我有一个小的 RACTest 应用 (听起来很熟悉?)我正在尝试进行单元测试.我想测试 添加了一个初始化程序信号用于其递增信号,而不仅仅是基于计时器.

I have a small RACTest app (sound familiar?) that I'm trying to unit test. I'd like to test MPSTicker, one of the most ReactiveCocoa-based components. It has a signal that sends a value once per second that accumulates, iff an accumulation flag is set to YES. I added an initializer to take a custom signal for its incrementing signal, rather than being only timer-based.

我想对 MPSTicker 的几个行为进行单元测试:

I wanted to unit test a couple of behaviours of MPSTicker:

  • 验证在启用累加时其累加信号是否正确递增(即单调递增)并且输入递增信号发送新值.
  • 验证它在输入信号发送值时发送相同的值(而不是递增的值).

我添加了一个使用内置的测试-在计时器中测试第一个增量,它按我的预期工作(尽管我正在寻求有关改进愚蠢的 RACSequence 初始化的建议,但我为了使用 @(1) 获取信号而做的)我想要的价值.)

I've added a test that uses the built-in timer to test the first increment, and it works as I expected (though I'm seeking advice on improving the goofy RACSequence initialization I did to get a signal with the @(1) value I wanted.)

我很难弄清楚我可以提供给 MPSTicker 的输入信号,我可以手动将值发送到.我正在设想这样的测试:

I've had a very difficult time figuring out what input signal I can provide to MPSTicker that I can manually send values to. I'm envisioning a test like:

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

我尝试使用 RACSubject 以便我可以使用 sendNext: 按我认为合适的方式推送值,但它没有像我预期的那样工作.这是两个损坏的测试:

I tried using a RACSubject so I can use sendNext: to push in values as I see fit, but it's not working like I expect. Here's two broken tests:

- (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 中,我从未看到 controlledSignalsendNext: 中的任何值通过我的 subscribeNext:> 阻止.

In testManualTimerTheFirst, I never see any value from controlledSignal's sendNext: come through to my subscribeNext: block.

testManualTimerTheSecond 中,我尝试使用 asynchronousFirstOrDefault: 调用从信号中获取第一个值,然后在我的主题上手动发送一个值,但该值没有't 通过,当 asynchronousFirstOrDefault: 超时时测试失败.

In testManualTimerTheSecond, I tried using the asynchronousFirstOrDefault: call to get the first value from the signal, then manually sent a value on my subject, but the value didn't come through, and the test failed when asynchronousFirstOrDefault: timed out.

我在这里遗漏了什么?

推荐答案

这可能无法准确回答您的问题,但可以让您深入了解如何有效地测试信号.到目前为止,我自己使用了 2 种方法:

This may not answer your question exactly, but it may give you insights on how to effectively test your signals. I've used 2 approaches myself so far:

XCTestCase 和 TRVSMonitor

TRVSMonitor 是一个小实用程序,它会在您运行断言时为您暂停当前线程.例如:

TRVSMonitor is a small utility which will pause the current thread for you while you run your assertions. For example:

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 和错误块结束时通知它停止等待以使其继续执行(因此其他测试也可以运行).这种方法具有不依赖静态超时的好处,因此您的代码可以根据需要运行.

As you can see, I'm telling the monitor to wait right after I subscribe and signal it to stop waiting at the end of subscribeNext and error blocks to make it continue executing (so other tests can run too). This approach has the benefit of not relying on a static timeout, so your code can run as long as it needs to.

使用 CocoaPods,您可以轻松地将 TRVSMonitor 添加到您的项目中:

Using CocoaPods, you can easily add TRVSMonitor to your project:

pod "TRVSMonitor", "~> 0.0.3"

Specta &期待

Specta 是一个 BDD/TDD(行为驱动/测试驱动)测试框架.Expecta 是一个提供更方便的断言匹配器的框架.它内置了对异步测试的支持.它使您能够使用 ReactiveCocoa 编写更具描述性的测试,如下所示:

Specta is a BDD/TDD (behavior driven/test driven) test framework. Expecta is a framework which provides more convenient assertion matchers. It has built-in support for async tests. It enables you to write more descriptive tests with ReactiveCocoa, like so:

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 { 的使用.简单地使用 ^ { 意味着同步测试.

Note the use of ^AsyncBlock {. Using simply ^ { would imply a synchronous test.

这里调用 done() 函数来表示异步测试的结束.我相信 Specta 在内部使用 10 秒超时.

Here you call the done() function to signal the end of an asynchronous test. I believe Specta uses a 10 second timeout internally.

使用 CocoaPods,您可以轻松添加 Expecta &观察:

Using CocoaPods, you can easily add Expecta & Specta:

pod "Expecta", "~> 0.2.3"
pod "Specta", "~> 0.2.1"

这篇关于使用 RACSubject 对 RACSignal 的简单用法进行单元测试的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆