Kiwi iOS上下文块的奇怪排序

时间:2013-04-12 23:00:42

标签: ios objective-c bdd objective-c-blocks kiwi

我有一个Kiwi spec文件,看起来像这样:

#import "Kiwi.h"
#import "MyCollection.h"

SPEC_BEGIN(CollectionSpec)

describe(@"Collection starting with no objects", ^{
    MyCollection *collection = [MyCollection new];

    context(@"then adding 1 object", ^{
        MyObject *object = [MyObject new];
        [collection addObject:object];
        it(@"has 1 object", ^{
            [collection shouldNotBeNil];
            [collection.objects shouldNotBeNil];
            [[theValue(collection.objects.count) should] equal:theValue(1)]; //failing test
        });

        context(@"then removing 1 object", ^{
            [collection removeObject:object];
            it(@"has 0 objects", ^{
                [[theValue(collection.objects.count) should] equal:theValue(0)]; //passing test
            });
        });
    });
});

SPEC_END

运行规范导致此代码行[[theValue(collection.objects.count) should] equal:theValue(1)];

出现一次失败

这是奇怪的部分 - 如果我从规范中移除整个context(@"then removing 1 object", ^{...})块,则上述测试通过。

这让我相信在失败的测试之前[collection removeObject:object]行正在执行。我有一种感觉,我可能会误解执行块的顺序。

任何建议都将不胜感激!

1 个答案:

答案 0 :(得分:10)

你是正确的,[collection removeObject:object]在失败的测试之前被执行。将Kiwi测试视为两次通过:

  1. 设置:测试文件中的代码从上到下执行以设置测试上下文和期望
  2. 执行:每个单元测试运行,基本上每it / specify个语句运行一次,并为每个测试重复正确的设置/拆卸代码
  3. 请注意,Kiwi测试文件中的大多数代码都被指定为发送到Kiwi函数的一系列块。任何不遵循Kiwi块模式的代码,例如初始化/修改collection变量的代码,都可能因此在意外时间执行。在这种情况下,所有的集合修改代码都是在设置测试时第一次执行时执行的,然后运行测试。

    解决方案

    使用collection修饰符声明__block,并使用beforeEach来实例化和修改collection对象:

    describe(@"Collection starting with no objects", ^{
        __block MyCollection *collection;
        beforeEach(^{
            collection = [MyCollection new];
        });
    
        context(@"then adding 1 object", ^{
            beforeEach(^{
                MyObject *object = [MyObject new];
                [collection addObject:object];
            });
            it(@"has 1 object", ^{
                ...
            });
    
            context(@"then removing 1 object", ^{
                beforeEach(^{
                    [collection removeObject:object];
                });
                it(@"has 0 objects", ^{
                    ...
    

    beforeEach块告诉Kiwi每单元测试专门运行给定代码一次,并且对于嵌套上下文,块将根据需要按顺序执行。所以Kiwi会做这样的事情:

    // run beforeEach "Collection starting with no objects"
    collection = [MyCollection new]
    // run beforeEach "then adding 1 object"
    MyObject *object = [MyObject new]
    [collection addObject:object]
    // execute test "has 1 object"
    [collection shouldNotBeNil]
    [collection.objects shouldNotBeNil]
    [[theValue(collection.objects.count) should] equal:theValue(1)]
    
    // run beforeEach "Collection starting with no objects"
    collection = [MyCollection new]
    // run beforeEach "then adding 1 object"
    MyObject *object = [MyObject new]
    [collection addObject:object]
    // run beforeEach "then removing 1 object"
    [collection removeObject:object]
    // execute test "has 0 objects"
    [[theValue(collection.objects.count) should] equal:theValue(0)]
    

    __block修饰符将确保通过所有这些块函数正确保留和修改collection对象引用。