mokacoding

unit and acceptance testing, automation, productivity

Expecta, a matcher library that speaks English

When writing tests, like any other code, we should always keep the reader in mind. Tests should be easy to read, they should read like prose. We should aim to have tests that could be used as the only documentation for the code.

We've recently seen how using Specta can help us writing better unit, and acceptance tests for our Objective-C projects, but we can go one step further.

The Specta team built a matcher library called Expecta. Expecta comes a number of expectation methods that provide developer writing tests the syntax and vocabulary they need to achieve the clarity they strive for so much.

I am so confident in the readability of tests written with Specta and Expecta that I'm gonna write the rest of this post as a spec:

SpecBegin(ExpectaPost)

describe(@"The expecta matcher libray", ^{
    context(@"has all the matchers you would expect", ^{
        it(@"can test for equality", ^{
            expect(@42).to.equal(@42);
        });

        it(@"can test for true and false", ^{
            expect(YES).to.beTruthy();
            expect(NO).to.beFalsy();
        });

        it(@"can test for nil", ^{
            expect(nil).to.beNil();
        });

        it(@"or not", ^{
            expect([NSObject new]).toNot.beNil();
            expect(YES).toNot.beFalsy();
            expect(@1).notTo.equal(@2);
        });
    });

    context(@"when it comes to arrays", ^{
        it(@"can test for count, emptiness and contained objects", ^{
            NSArray *anArray = @[ @"red", @"yellow", @"pink", @"black", @"blue" ];

            expect(anArray).toNot.beEmpty();
            expect(anArray).to.haveCountOf(5);
            expect(anArray).to.contain(@"black");
        });
    });

    context(@"and when it comes to strings", ^{
        __block NSString *aString = @"lorem ipsum dolor sit amet";

        it(@"is not gonna disappoint you", ^{
            expect(aString).to.beginWith(@"lorem");
            expect(aString).to.endWith(@"amet");
        });

        it(@"can even match using regular expressions", ^{
            expect(aString).to.match(@"lorem i.* dolor|marameo");
        });
    });

    it(@"also has powerful assertions for numbers", ^{
        expect(42).to.beLessThanOrEqualTo(50);
        expect(99).to.beCloseToWithin(100, 3);
        expect(97).to.beInTheRangeOf(90, 100);
    });

    context(@"and there's more", ^{
        it(@"allows us to asert object's properties", ^{
            expect([Orange class]).to.beSubclassOf([Fruit class]);
            expect([Orange class]).to.respondTo(@selector(squeeze));
        });

        it(@"and even test for code that raises exceptions", ^{
            expect(^{
                // Note: the compiler already throws warning here,
                // but for the sake of the example will behave like silly developers and ignore it
                NSMutableArray *immutableArray = [NSArray array];
                [immutableArray addObject:@"a"];
            }).to.raiseAny();
        });
    });

    context(@"and all the above can be done asyncronously!", ^{
        Pizza *quattroStagioni = [PizzaDelivery bringMePizza];
        expect([quattroStagioni hasBeenDelivered]).will.beTruthy();
    });
});

SpecEnd

What we just saw is only a subset of the matchers available with Expecta, you can find the full list on the project's README.

Expecta is a very nice library, and I hope this post triggered some interest in you towards unit testing and the art of readable code, but it does not end here! You can actually extent Expecta by providing custom matchers. We are going to look at those next week, so don't forget to subscribe.