D LanguageUnittesting

Syntax

  • unittest { ... } - a block that is only run in "unittesting" mode
  • assert(<expression that evaluates to a boolean>, <optional error message>)

Unittest blocks

Tests are an excellent way to ensure stable, bug-free applications. They serve as an interactive documentation and allow to modify code without fear to break functionality. D provides a convenient and native syntax for unittest block as part of the D language. Anywhere in a D module unittest blocks can be used to test functionality of the source code.

/**
Yields the sign of a number.
Params:
    n = number which should be used to check the sign
Returns:
    1 for positive n, -1 for negative and 0 for 0.
*/
T sgn(T)(T n)
{
    if (n == 0)
        return 0;
    return (n > 0) ? 1 : -1;
}

// this block will only be executed with -unittest
// it will be removed from the executable otherwise
unittest
{
    // go ahead and make assumptions about your function
    assert(sgn(10)  ==  1);
    assert(sgn(1)   ==  1);
    assert(sgn(-1)  == -1);
    assert(sgn(-10) == -1);
}

Executing unittest

If -unittest flag is passed to the D compiler, it will run all unittest blocks. Often it is useful to let the compiler generate a stubbed main function. Using the compile & run wrapper rdmd, testing your D program gets as easy as:

rdmd -main -unittest yourcode.d

Of course you can also split this process into two steps if you want:

dmd -main -unittest yourcode.d
./yourcode

For dub projects compiling all files and executing their unittest blocks can be done conveniently with

dub test

Pro tip: define `tdmd` as shell alias to save tipping.
alias tdmd="rdmd -main -unittest"

and then test your files with:

tdmd yourcode.d

Annotated unittest

For templated code it is often useful to verify that for function attributes (e.g. @nogc are inferred correctly. To ensure this for a specific test and thus type the entire unittest can be annotated

@safe @nogc pure nothrow unittest
{
    import std.math;
    assert(exp(0)  ==  1);
    assert(log(1)  ==  0);
}

Note that of course in D every block can be annotated with attributes and the compilers, of course, verifies that they are correct. So for example the following would be similar to the example above:

unittest
{
    import std.math;
    @safe {
        assert(exp(0)  ==  1);
        assert(log(1)  ==  0);
    }
}