Code Reuse in Kiwi Tests

December 7, 2012

Kiwi provides a pretty awesome DSL for writing expressive, executable specs. What it doesn't provide is an easy way to reuse code.

Sure, it has beforeEach() and friends. And this is usually the path people start down, but I'm here to tell you that that way be madness.

Unlike RSpec, you can't put methods in your spec (well, you actually can store blocks to reuse if you want to brave the madness of the syntax):

    ___block int customers;

    beforeEach(^{
        customers = 0;
    });

    void (^ has_n_customers) (int) = ^(int number_of_customers) {
        customers = number_of_customers;
    };

    it(@"works with three customers", ^{
        has_n_customers(3);
        ...
    });

I've actually found that writing a tester class is the best solution:

    @interface FooTester
    @property (readonly) Foo* foo;
    @property (readonly) UIVIew *view;
    @end

    SPEC_BEGIN(FooSpec)
    ...
    SPEC_END

    @implementation FooTester

    - (id)init
    {
        [super init];
        _foo = [[[Foo alloc] init] autorelease];
        _view = [UIView nullMock];
        [_foo setView:_view];
        return self;
    }

    @end

This sure feels like a lot of work and a lot of noise, but I've found throughout my current project that this strategy almost always produces cleaner, more readable specs with less duplication.

If you're curious, I had a few nice pre-ARC strategies which no longer work. My first alternative involved using a C struct returned from a C function to set up the class under test and its mocked dependencies. My second alternative is to use a C++ class and make the tests Objective-C++. You can usefully build DSLs by chaining C++ methods.

Tags: iOS objvimmer kiwi