Only 1 expectation per spec – an empty dogma or not?

An old TDD dogma says that there should be only a single expectation per spec 1.

I agree that this is a good rule of thumb, but as with all such extreme advices, you shouldn’t follow it blindly. To better understand why the single expectation heuristic is useful, let’s focus on the impact it has on the two core aspects of BDD-style specs: acting as living documentation and guiding the design of the system.

Specs as living documentation

To be useful as living documentation, our specs must be, above all, readable. Does the number of expectations per spec have any impact on readability? To answer this, let’s consider what may cause our spec to contain more than one expectation. There are two possible cases:

Mixing two concerns in a single spec

Mixing two unrelated concerns usually results in a spec like this:

describe('Anti-spam bot', function() {
    it('should ban spammer and notify admin', function() {
        antiSpamBot.checkOut(spammer);

        expect(spammer).toBeBanned();
        expect(admin).toBeNotified();
    });
});

There are two actions (banning and notifying), two subjects (spammer and admin) and the logical operator “and”. All this makes the spec unnecessarily complicated and more difficult to read (and also harder to pinpoint the problem when the spec fails, but I won’t focus on this aspect here).

We may try do remove the complication from the test name by making it more general, like it('should handle spammer correctly', ...);, but such a name makes bad documentation, because it hides the details of what actions should happen.

But let’s see what would happen if we allowed only a single expectation per spec. This forces us to refactor the spec like this:

describe('Anti-spam bot', function() {
    beforeEach(function() {
        antiSpamBot.checkOut(spammer);
    });

    it('should ban spammer', function() {     
        expect(spammer).toBeBanned();
    });

    it('should notify admin', function() {        
        expect(admin).toBeNotified();
    });
});

Now concerns are separated and spec names more concise and cleaner, which increases the readability and the value of our specs as documentation.

Now let’s consider the second case which may result in more than one expectation per spec:

A single concern with many related assertions

Imagine a spec like this:

describe('User', function() {
    it('should have a non empty address', function() {
        var address = user.address;

        expect(address.streetName).not.toBeEmpty();
        expect(address.streetNumber).not.toBeEmpty();
    });
});

Despite containing two expectations, this spec is free from all the problems from the previous example, because it still covers only one concern. It has a single subject and no “ands”, and is very terse and readable. It also doesn’t hide any behavior – User should have a non empty address is a precise specification, what elements the address consists of is an implementation detail that is irrelevant from the documentation point of view.

However, although it’s an example of a readable spec, it’s also an example of not so well designed code. What brings us to the second important aspect of the BDD-style specs:

Specs guiding the design of the system

There is a smell in this code. The User class is polluted by several address related fields. Why do they belong directly to User? (BTW: if you look closely at the spec, you can see another, more subtle smell – breaking The Law of Demeter).

Again, let’s see what would happen if we listened to the single expectation per spec rule. How can we verify if a user has non empty address, if we’re not allowed to check more than one address field at a time?

It’s quite simple – we can treat what our spec describes (User should have a non empty address – or even simpler: User should have an address) literally:

describe('User', function() {
    it('should have an address', function() {
        expect(user.address).not.toBeNull();
    });
});

describe('Address', function() {
    it('should have non empty street name', function() {
        expect(address.streetName).not.toBeEmpty();
    });

    it('should have non empty street number', function() {
        expect(address.streetNumber).not.toBeEmpty();
    });
});

This design is much better. User now has a single Address field (that can either exist or not, so the check becomes much simpler). Address becomes a separate class, that is suitable for reuse, can include its own validation rules and is much easier to test directly than through the User class. (And we’ve also solved The Law of Demeter problem).

All of these benefits came because we’ve “listened to our specs” – particularly, to the fact that there is more than one expectation.

Must there ALWAYS be only one expectation?

Of course not. As with any rule, it’s not a universal law, and it has its boundaries. There are legitimate cases, when it’s more readable to use many expectations per spec (more about this in another post).

However, more than one expectation in a spec is a smell you shouldn’t ignore (especially if it is accompanied by complex specs with “ands”). Usually, it tries to tell you that you mix two independent concerns in your spec, or that there is a hidden class, waiting to be revealed.

What about you? Do you try to adhere to the “one expectation per spec rule”? How strictly? Please share below in the comments!


  1. or a single assertion per test, depending on which nomenclature you use – in this post I’ll stick to the BDD style 
Advertisements

What do you think?

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s