Boost C++ Libraries Home Libraries People FAQ More

PrevUpHomeNext

Running Selected Tests

As we continue to add unit tests for a library, we can get quite a large number of tests. If they are true unit tests and each test exercises a single class isolated from its collaborators, then most of the tests are not relevant to the changes we are making. The test executable accepts a number of command-line arguments that allow us to control the behavior of the test framework, including which tests to run.

We can list the available tests in a test executable with --report_level set to detailed:

> test_hello --report_level=detailed
Running 2 test cases...

Test suite "Master Test Suite" passed with:
  2 assertions out of 2 passed
  2 test cases out of 2 passed

  Test case "hello_world_inserts_text" passed with:
    1 assertion out of 1 passed

  Test case "hello_world_stream_with_badbit_throws_runtime_error" passed with:
    1 assertion out of 1 passed

[Caution] Caution

The arguments to the test executable, such as --report_level, use an underscore (_) to separate words, not a dash (-).

Suppose we are continuing to make changes to hello_world and we want to run only the tests that apply to that function. We can easily identify the relevant tests in the output of --report_level because we have used a test naming convention of prefixing all test case names with the name of the system under test, hello_world.

Using the --run_test argument, we can specify a comma-separated list of test case names to run:

> test_hello --run_test=hello_world_inserts_text,hello_world_stream_with_badbit_throws_runtime_error
Running 2 test cases...

*** No errors detected

EXIT STATUS: 0

Wow, that's really a mouthfull! Even with command recall, typing this command for the first time will require typing the exact names of all the test cases for hello_world. As we add more test cases, we'll have to extend the command line argument to account for each new test case.

We can solve this problem by using test suites, which organize tests into a named group:

struct hello_world_fixture
{
    std::ostringstream dest;
};

BOOST_AUTO_TEST_SUITE(test_hello);

#define HELLO_WORLD_TEST_CASE(name_) \
    BOOST_FIXTURE_TEST_CASE(hello_world_##name_, hello_world_fixture)

HELLO_WORLD_TEST_CASE(inserts_text)
{
    hello_world(dest);

    BOOST_REQUIRE_EQUAL("Hello, world!\n", dest.str());
}

HELLO_WORLD_TEST_CASE(stream_with_badbit_throws_runtime_error)
{
    dest.clear(std::ios_base::badbit);

    BOOST_REQUIRE_THROW(hello_world(dest), std::runtime_error);
}

BOOST_AUTO_TEST_SUITE_END();

Now --report_level outputs the following:

> test_hello --report_level=detailed
Running 2 test cases...

Test suite "Master Test Suite" passed with:
  2 assertions out of 2 passed
  2 test cases out of 2 passed

  Test suite "test_hello" passed with:
    2 assertions out of 2 passed
    2 test cases out of 2 passed

    Test case "hello_world_inserts_text" passed with:
      1 assertion out of 1 passed

    Test case "hello_world_stream_with_badbit_throws_runtime_error" passed with:
      1 assertion out of 1 passed

The indentation shows that the test cases for hello_world are organized under the test suite named test_hello_world.

[Note] Note

We can't name the suite hello_world because that would conflict with the name of our system under test, as both are declared in the same global namespace.

Now we can supply the name of the test suite to --run_test to run all the test cases in the suite:

> test_hello --run_test=test_hello
Running 2 test cases...

*** No errors detected

EXIT STATUS: 0

As we add more test cases to the suite, we don't have to change the command we are using to run the tests and we don't need to remember the exact names of the test cases.

Suites arrange test cases into a hierarchy. You can have suites within suites to provide larger groupings to test a collection of related classes together. Once a test case is part of a suite, we must use the name of the enclosing suite with the name of the test case if we wish to run a single test case within the suite by name:

> test_hello --run_test=test_hello/hello_world_inserts_text
Running 1 test case...

*** No errors detected

The names of the enclosing suites are separated from each other and from the test case name with a slash (/). The names supplied to --run_test also allow for wildcard matching:

> test_hello --run_test=hello_world/*inserts_text
Running 1 test case...

*** No errors detected

Just as we can use a fixture with a test case to eliminate repeated setup and tear down, we can use a fixture with a test suite. All test cases in the test suite will derive from the test suite's fixture. If a test case in a test suite with a fixture specifies its own fixture, then the test case derives from the fixture specified on the test case and not on the test suite. If you want the test case to use both fixtures, then make your test case fixture derive from the test suite fixture.

We can use a test suite fixture for test_hello to take care of the duplicated setup between test cases. Since our test cases are now within the scope of a suite, we don't need to give them a unique prefix anymore. Refactoring the tests to use a suite fixture and discarding the prefix gives us the following code:

#define BOOST_TEST_MAIN
#include <boost/test/included/unit_test.hpp>
#include "hello.hpp"
#include <ios>
#include <sstream>

struct hello_world_fixture
{
    std::ostringstream dest;
};

BOOST_FIXTURE_TEST_SUITE(test_hello, hello_world_fixture);

BOOST_AUTO_TEST_CASE(inserts_text)
{
    hello_world(dest);

    BOOST_REQUIRE_EQUAL("Hello, world!\n", dest.str());
}

BOOST_AUTO_TEST_CASE(stream_with_badbit_throws_runtime_error)
{
    dest.clear(std::ios_base::badbit);

    BOOST_REQUIRE_THROW(hello_world(dest), std::runtime_error);
}

BOOST_AUTO_TEST_SUITE_END();

Now --report_level outputs the following:

> test_hello.exe --report_level=detailed
Running 2 test cases...

Test suite "Master Test Suite" passed with:
  2 assertions out of 2 passed
  2 test cases out of 2 passed

  Test suite "test_hello" passed with:
    2 assertions out of 2 passed
    2 test cases out of 2 passed

    Test case "inserts_text" passed with:
      1 assertion out of 1 passed

    Test case "stream_with_badbit_throws_runtime_error" passed with:
      1 assertion out of 1 passed

Example Source Code

PrevUpHomeNext