简体   繁体   English

如何对依赖Objective-C中的易失变量的方法进行单元测试?

[英]How can I unit test a method that depends on a volatile variable in Objective-C?

Here's a simplified version of my class: 这是我班的简化版:

@interface RTMovieBuilder : NSObject
@property (atomic, getter = isCancelled) volatile BOOL cancelled;
@property (nonatomic, weak) id<BuilderDelegate>delegate;
- (void)moviesFromJSON:(id)JSON;
- (Movie *)movieFromDictionary:(NSDictionary *)dict;
- (void)cancel;
@end

@implementation RTMovieBuilder

- (void)moviesFromJSON:(id)JSON
{
    // Check for errors -> If good, then do...

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
        [self syncrouslyCreateMoviesFromJSON:JSON];
        });
}

- (void)syncrouslyCreateMoviesFromJSON:(id)JSON
{        
    NSMutableArray *movies = [NSMutableArray array];

    for (NSDictionary *dict in JSON)
    {
        if ([self isCancelled])
            return;
        else
            [movies addObject:[self movieFromDictionary:dict]];
    }

    [self notifyDelegateCreatedObjects:movies];
}

- (Movie *)movieFromDictionary:(NSDictionary *)dict
{
    Movie *movie = [[Movie alloc] init];
    // Set movie properties based on dictionary...
    return movie;
}

- (void)cancel
{
    [self setCancelled:YES];
}

// ... Other methods omitted for brevity's sake

@end

The property cancelled is atomic and volatile because it may be accessed by other threads (ie the main thread may call cancel method to stop the operation). cancelled的属性是atomic volatile因为它可以被其他线程访问(即主线程可以调用cancel方法来停止操作)。 (I believe these are needed, if not, please note why it's not in your answer.) (我认为这些是必需的,如果不需要,请注意为什么它不在您的答案中。)

I am trying to write unit tests to make sure this will work before writing the view controller class. 我正在尝试编写单元测试,以确保在编写视图控制器类之前可以进行测试。

How can I write a unit test that will simulate a call to cancel while RTMovieBuilder is in the middle of creating movies ? 如何在RTMovieBuilder制作movies的过程中编写一个单元测试来模拟cancel通话的单元测试?

Edit 编辑

Here's a unit test I have already written which tests to make sure that notifyDelegateCreatedObjects: isn't called if cancel is called first. 这是我已经编写的单元测试,以确保如果先调用cancel则不会调用notifyDelegateCreatedObjects:

- (void)testIfCancelledDoesntNotifyDelegateOfSuccess
{
    // given
    RTMovieBuilder *builder = [[RTMovieBuilder alloc] init];
    builder.delegate = mockProtocol(@protocol(BuilderDelegate));

    // when
    [builder cancel];
    [builder notifyDelegateCreatedObjects:@[]];

    // then
    [verifyCount(builder.delegate, never()) builder:builder createdObjects:anything()];
}

I'm using OCHamcrest and OCMockito . 我正在使用OCHamcrestOCMockito This test passes. 该测试通过。

I would avoid trying to simulate thread timing in unit tests and focus more on figuring out what all the possible end states could be regardless of where the timing falls, and write tests for code under those conditions. 我将避免尝试在单元测试中模拟线程时序,而将精力更多地放在弄清无论时序落在何处,所有可能的最终状态可能是什么,并在这些条件下编写代码测试。 This avoids endless complexity in your tests, as bbum points out as well. bbum指出,这避免了测试的无限复杂性。

In your case it seems the condition you need to be testing for is if the call to notifyDelegateCreatedObjects happens after the action is canceled, because the cancel came too late. 在您的情况下,似乎需要测试的条件是,在取消操作之后是否发生对notifyDelegateCreatedObjects的调用,因为取消来得太迟了。 So instead just unit test the handling of that scenario downstream in your notifyDelegateCreatedObjects method, or whatever class is being notified of that aborted event because of the thread timing. 因此,只需在notifyDelegateCreatedObjects方法中对该单元的下游处理进行单元测试,或者由于线程计时而正在通知该中止事件的任何类。

I know this is not a specific answer to your question but I think its a better approach to achieve the same unit testing goal. 我知道这不是您问题的具体答案,但我认为这是达到相同单元测试目标的更好方法。

There is no reason to use volatile if your property is atomic and you always go through the setter/getter. 如果您的属性是atomic属性,并且您总是经过设置器/获取器,则没有理由使用volatile

As well, this is a bit of re-inventing the wheel, as noted in the comments. 同样,如评论中所述,这是在重新发明轮子。

In general trying to unit test cancellation with any hope of full coverage is very hard because you can't really effectively test all possible timing interactions. 通常,很难对完全覆盖的希望进行单元测试取消,因为您不能真正有效地测试所有可能的计时交互。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM