Part six of a series of posts about automated testing.

There is a guideline for structuring individual unit tests, known as “Arrange, Act, Assert”. The idea is, most tests should be structured in this manner:

sub one_plus_one_should_give_two : Test {
    # Arrange
    my $calc = Calculator->new();

    # Act
    my $result = $calc->run("1 + 1");

    # Assert
    is($result, 2);
}

That is, these steps are clearly separated, and there is usually a single assertion at the end.

When more than one test has a similar “Arrange” step, it is often possible to factor out a setup method:

sub setup : Test(setup) {
    shift->{calc} = Calculator->new();
}

sub one_plus_one_should_give_two : Test {
    my $result = shift->{calc}->run("1 + 1");

    is($result, 2);
}

In general, it can be useful to shorten tests by writing helper methods for common boilerplate steps. This maximizes the signal-to-noise ratio of the test itself - you see just the condition, and the assertion.

There are potentially some exceptions to the idea that you have one assertion per test. For example, tests which specifically exercise changes in state:

my ($user, $srv);
sub setup : Test(setup) {
    $user = User->new();
    $srv = LoginService->new( db => get_db() );
}

sub three_failed_login_attempts_should_lock_account : Test {
    $srv->login( $user, get_invalid_password() );
    is( $srv->is_account_locked( $user ), 0 );
    $srv->login( $user, get_invalid_password() );
    is( $srv->is_account_locked( $user ), 0 );
    $srv->login( $user, get_invalid_password() );
    is( $srv->is_account_locked( $user ), 1 );    
}

The key is to have one test per behaviour.

Further reading