Part five of a series of posts about automated testing.

We are building a self-driving car in Perl. It will pick up jobseekers, and play upbeat music as it drives them to their job interviews. Consider this early code sample:

# Code
package Car;

use Moo;

has 'radio_station' => (is => 'ro');
has 'handbrake_status' => (is => 'ro');

sub drive {
    my ($self) = @_;
    $self->ensure_handbrake_off();
    $self->turn_wheels();
    $self->start_radio();
}

...

And a first stab at the tests:

# Tests
sub test_drive : Tests {
    my $car = Car->new(
        handbrake_status => 'on',
        radio_station => 'BBC Radio 1',
    );
    $car->drive();
    is( $car->handbrake_status(), 'off' );
    is( $car->speed(), '60' );
    is( $car->radio_playing(), 'BBC Radio 1' );
}

As more functionality gets added, the test becomes harder to read. As more tests accumulate like this, a change to an obscure feature such as the radio can require changes to hundreds of long tests. Instead, aim to write one test per behaviour, not per method:

# Tests

sub drive_should_disable_handbrake : Test {
    my $car = Car->new( handbrake_status => 'on' );
    $car->drive();
    is( $car->handbrake_status(), 'off' );
}

sub drive_should_make_car_go_fast : Test {
    my $car = Car->new();
    $car->drive();
    is( $car->speed(), '60' );
}

sub drive_should_turn_on_radio : Test {
    my $car = Car->new( radio_station => 'BBC Radio 1' );
    $car->drive();
    is( $car->radio_playing(), 'BBC Radio 1' );
}

This produces shorter, more readable tests, which are easier to adapt to new behaviours.

In addition, the tests end up describing the behaviour of the system under test, which can help with understanding unfamiliar code. This relates closely to yesterday’s post about testing reusable interfaces.

Further reading