Behaviour Focused Tests with Given/When/Then

When writing unit tests it is important to keep in mind that the purpose of any unit test is to validate behaviour of the system under test.  A good unit test is focused on one specific aspect of the behaviour of the system.  That way, when a test fails it is clear what particular functionality is out of specification.

A unit test can give you some clues when your tests are unfocused.  For instance, having more than one or two asserts in the test is a clue that you are testing more than aspect at a time.  Just like production software should only have one reason to change, unit test code should have only one reason to fail.

Another hint that there is room for improvement is where a test has complicated setup code or code duplication.  If there is a lot of work involved creating a suitable test environment for the unit under test it is usually a sign that a class or module has too many responsibilities.

One helpful way to keep your tests focused on behaviour is to follow a given/when/then template.  This technique started in the Behaviour Driven Development (BDD) approach proposed by Dan North and Joe Waines.  As with most testing styles this began life in the Java and Ruby communities, sprouting a plethora of frameworks, but it can equally be applied to testing C and C++ code.

Here is an example of a unit test that is testing an embedded C module.

TEST_F(PublishMfgInfoTestSuite, when_allocate_cmdbuf_fails_no_eeprom_read_performed)
{
    // given
    allocateShouldSucceed = false;
    // when
    MFGI_sendMfgInfoToBox((DispArg_t)pCmdBuffer);
    // then
    ASSERT_EQ((uint32_t)0, VEEP_getSectionCrcAndLocation_callCount);
    ASSERT_EQ((uint32_t)0, VEEP_readPart_callCount);
}

There are a number of interesting points to note here.  Firstly, the name of the test clearly describes the functionality that is expected.  If you were using a Ruby DSL to describe the tests it might read like “given an allocation failure when sending MFG info to BOX then no EEPROM reading will be performed”.  If this test fails you can tell immediately how the software under test failed to meet requirements.

Secondly, the body of the test contains some comments.  These are entirely optional, but I find it useful to keep them around after I have finished writing the test as a reminder for the future me.  The first, given, is used describe the initial environment for executing the test.  The when clause is where the execution of the functionality under test is performed.  Finally, the then state is where the expectations or end state of the test are asserted.

By following the given/when/then template and being conscientious in our test naming we have arrived at a test absolutely focused on one particular behaviour of the system.

An important part of any technique is understanding when to not use it.  Some people advocate using a given/when/then structure to your test names.  In my opinion (and many others) this leads to verbose naming and unnecessary noise.  As with any recipe, context is king and you should use your own judgement to decide what will give the most readable and focused tests.

Further Reading:

http://blog.dannorth.net/introducing-bdd/

http://en.wikipedia.org/wiki/Behavior_Driven_Development

http://blog.objectmentor.com/articles/2009/12/19/the-polyglot-tester

Leave a comment

Filed under software, testing

Leave a Reply

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