Skip to content

Best Practices

Robert Coltheart edited this page Feb 23, 2022 · 11 revisions

Contents

  1. General advice
    1. Consider using Subject to enhance test output
    2. Prefer nested context classes over inherited classes
    3. Behavior usage is discouraged
  2. Delegate usage
    1. It should be a single line statement
    2. Because should be a single line statement
    3. Stick to Arrange-Act-Assert conventions
  3. Naming conventions
    1. Consider naming contexts and specs from a users' point-of-view
    2. Test classes should be snake_cased
    3. Fields in a context should be snake_cased
  4. Formatting
    1. Do not use visibility modifiers
    2. Consider putting lambda expression on new line
    3. Do not use braces for single-line lambdas
    4. Fields should appear first in a class

General advice

Consider using Subject to enhance test output

The Subject attribute will be used in formatting the test run output so it's advisable to use it to customize the display. Typically it should identify the name or type of the class under test.

It will also guide ReSharper (if you use it) to not mark any It delegates as 'unused' fields.

Right:

[Subject(typeof(Car))]

Prefer nested context classes over inherited classes

Using nested context classes is preferred to using base classes for the following reasons:

  • It improves readability as all test code is in the one place
  • Establish delegates can be used to 'share' setup of the class under test
  • Tests can be grouped sensibly

However, be warned that too much nesting of classes can also be confusing, so try to strike a balance. A good rule of thumb is to nest no more than 2 or 3 classes deep in your tests.

Consider the following plain-text specification: When driving a car and the car moves forward and radio is on, upon reaching a traffic light, the car stops. This statement could necessitate 3 nested classes and really stretches the limit of a sensible English grammar!

Right:

class When_driving_a_car
{
    Establish context = ... ;

    class when_the_car_moves_forward
    {
        Establish context = ... ;
    }
}

Behavior usage is discouraged

The Behaviors feature is only maintained for backwards compatibility, and usage is discouraged for the following reasons:

  • No strong typing between behavior subjects hides the fact that the wrong type may have been used
  • No compilation checking of the behavior subject

Delegate usage

It should be a single line statement

Right:

It should_turn_off_engine = () =>
    stopped.ShouldBeTrue();

Wrong:

It should_turn_off_engine = () =>
{
    var stopped = car.Stop();

    stopped.ShouldBeTrue();
}

Because should be a single line statement

Right:

Because of = () =>
    car.StartEngine();

Wrong:

Because of = () =>
{
    car = new Car();
    
    var key = new Key();

    car.StartEngine(key);    
}

Stick to Arrange-Act-Assert conventions

Establish is for arrange, Because is for acting, and It should be used for asserting.

Naming conventions

Consider naming contexts and specs from a users' point-of-view

Tests should roughly follow language grammar and should read with/when such a state, when something happens, it should be xyz.

Right:

class When_using_a_car
{
    static bool is_stopped;

    Because of = () =>
        is_stopped = car.StopCar();

    It should_turn_off_the_engine = () =>
        is_stopped.ShouldBeTrue();
}

Wrong:

class Car_tests
{
    static bool value;

    Because of = () =>
        value = car.StopCar();
    
    // What should be true?? The test output will be ambiguous.
    It should_be_true = () =>
        value.ShouldBeTrue();
}

Test classes should be snake_cased

The test class should begin with a conjunction such as when, and be snake_cased.

Right:

class When_using_a_car
{
}

Wrong:

class WhenUsingACar
{
}

Fields in a context should be snake_cased

Right:

class When_using_a_car
{
    static Car car;
    static bool is_stopped;
}

Wrong:

class When_using_a_car
{
    static Car _car;
    static bool isStopped;
}

Formatting

Do not use visibility modifiers

Right:

class When_using_a_car
{
    static bool value;

    It should_be_true = () =>
        value.ShouldBeTrue();
}

Wrong:

public class When_using_a_car
{
    private static bool value;

    private It should_be_true = () =>
        value.ShouldBeTrue();
}

Consider putting lambda expression on new line

Recommended:

 It should_be_true = () =>
    value.ShouldBeTrue();

Do not use braces for single-line lambdas

Right:

 It should_be_true = () =>
    value.ShouldBeTrue();

Wrong:

It should_be_true = () =>
{
    value.ShouldBeTrue();
};

Fields should appear first in a class

Right:

class When_using_a_car
{
    static bool value;

    It should_be_true = () =>
        value.ShouldBeTrue();
}

Wrong:

class When_using_a_car
{
    It should_be_true = () =>
        value.ShouldBeTrue();

    static bool value;
}