igloo - BDD Style Unit Testing for C++

Igloo test applications are created as command line executables that either succeeds with a return value of zero, or fails with a return value corresponding to the number of failing tests.

Test Application Basics

Your test applicaton should look as follows:

1 #include <igloo/igloo.h>
2 using namespace igloo;
3 
4 int main(int argc, const *argv[])
5 {
6   return TestRunner::RunAllTests(argc, argv);
7 }

This will automatically run all registered tests in the application.

Command Line Switches

If you pass argc and argv to TestRunner::RunAllTests(), your test application supports the following command line switches:

--version                          Print version of Igloo and exit.
--help                             Print available command line switches.
--output=[default|vs|color|xunit]  Select output format.
    default: Igloo's default output format
    vs:      Visual Studio's output format
    color:   Colored output format
    xunit:   XUnit style output format

Contexts

Igloo considers a test application to be an executable specification of the application that’s under test. The test application contains one or more contexts that each describe a state of the application under test and a set of specifications for what should hold true in that context.

 1 #include <igloo/igloo.h>
 2 using namespace igloo;
 3 
 4 Context(a_newly_started_game)
 5 {
 6   Spec(should_have_an_empty_board)
 7   {
 8     Assert::That(game.Positions(), Has().All().EqualTo(EmptyPosition));
 9   } 
10 
11   Game game;
12 };

Alternatives

Besides Context/Spec there are other syntaxes available out of the box: Describe/It and When/Then. You enable these by including igloo_alt.h instead of igloo.h. If you like to, you can even define your own syntax for describing a test.

 1 #include <igloo/igloo_alt.h>
 2 using namespace igloo;
 3 
 4 Describe(a_newly_started_game)
 5 {
 6   It(has_an_empty_board)
 7   {
 8     Assert::That(game.Positions(), Has().All().EqualTo(EmptyPosition));
 9   } 
10 
11   Game game;
12 };
13 
14 When(a_new_game_is_started)
15 {
16   Then(it_should_have_an_empty_board)
17   {
18     Assert::That(game.Positions(), Has().All().EqualTo(EmptyPosition));
19   } 
20 
21   Game game;
22 };

Nested Contexts

Igloo enables you to create nested contexts. The inner context inherits and augments the properties of the outer context. This is a powerful feature that lets you organize your contexts in a way that enables you to create just the right amount of setup for each context.

 1 #include <igloo/igloo_alt.h>
 2 using namespace igloo;
 3 
 4 Describe(a_newly_started_game)
 5 {
 6   It(has_an_empty_board)
 7   {
 8     Assert::That(game.Positions(), Has().All().EqualTo(EmptyPosition));
 9   }
10 
11   Describe(player_one_is_selected_to_start)
12   {
13     void SetUp()
14     {
15       Root().game.Select(PlayerOne);
16     }
17 
18     It(is_player_ones_turn)
19     {
20       Assert::That(Root().game.NextPlayer(), Equals(PlayerOne));
21     }
22   };  
23 
24   Game game;
25 };

Accessing Parent Contexts

A nested context can access members of its parent contexts by using the following methods:

Parent() accesses the direct parent context of the current context.

Root() accesses the root (outermost) context. Most of the time all of the members needed by a set of nested contexts are declared in the root context. No matter how deep your nesting goes, you can access those members by using this method.

Set Up and Tear Down

Igloo creates a new context before each call to a Spec. This ensures that each Spec is executed in a fresh environment. Sometimes you might need to perform additional setup before each call, and additional cleanup after each call. You can do this by overriding the methods SetUp and TearDown in your context.

 1 #include <igloo/igloo.h>
 2 using namespace igloo;
 3 
 4 Context(NameOfContext)
 5 {
 6   void SetUp() { /* Setup code. Called before each Spec. */ }
 7   void TearDown() { /* Tear down code. Called after each Spec. */ }
 8 
 9   // ...
10 };

Context SetUp and TearDown

Sometimes you need to setup an environment that is the same for all specs in a context, and that takes a bit too long to setup before each call. In this case you can use Igloo’s SetUpContext and TearDownContext methods to set up static members used in the specs later on.

1 Context(name_of_context)
2 {
3   static void SetUpContext() { /* Called once before any Spec. */ }
4   static void TearDownContext() { /* Called once after all Specs. */ }
5 
6   // ...
7 
8   static MyEnvironment common_stuff;
9 };

Running a Subset of the Tests

Specifying which tests to run

By appending “_Only” to a context or a spec, igloo will only run those contexts and specs that has the “_Only” suffix. If a context is marked as “_Only” it will also run all its nested contexts.

 1 Context_Only(name_of_context)
 2 {
 3   // This context will be run
 4 
 5   Context(name_of_nested_context)
 6   {
 7     // This context will be run as well
 8   };
 9 };
10 
11 Context(another_context)
12 {
13   Spec_Only(my_spec)
14   {
15     // This spec will be run
16   }
17 
18   Spec(my_other_spec)
19   {
20     // This will not be run.
21   }
22 };
23 
24 Context(yet_another_context)
25 {
26   // This context will not be run
27 };

Specifying which tests to skip

You can append “_Skip” to contexts and specs to exclude them from the test runs.

 1 Context_Skip(skip_this_context)
 2 {
 3 };
 4 
 5 Context(a_context)
 6 {
 7   Spec_Skip(skip_this_spec)
 8   {
 9   }
10 };

Test Listeners

You can listen to events during a test run to perform additional work such as logging, statistics, or additional setup and teardown. You do this by deriving from TestListener:

 1 class TestListener
 2 {
 3   public:
 4     // Called when a test run is about to begin.
 5     virtual void TestRunStarting() = 0;
 6 
 7     // Called after a test run has been completed.
 8     // The TestResults parameter contains information about the
 9     // test run, and stores collections of all succeeded and failed specs.
10     virtual void TestRunEnded(const TestResults& results) = 0;
11 
12     // Called before the specs in a context is about to be called.
13     // If you’ve added ContextAttributes to your contexts you can read these here.
14     //
15     // Context(MyContext)
16     // {
17     //  ContextAttribute("category", "my category")
18     // 
19     //  // ...
20     // };
21     //
22     // void ContextRunStarting(const ContextBase& context)
23     // {
24     //   const std::string& category = context.GetAttribute("context");
25     //
26     //   // ...
27     // }
28     virtual void ContextRunStarting(const ContextBase& context) = 0;
29 
30     // Called after all specs have been called for a context.
31     virtual void ContextRunEnded(const ContextBase& context) = 0;
32 
33     // Called before a spec is about to be executed.
34     virtual void SpecRunStarting(const ContextBase& context, const std::string& specName) = 0;
35 
36     // Called after a spec has been successfully executed.
37     virtual void SpecSucceeded(const ContextBase& context, const std::string& specName) = 0;
38 
39     // Called after a spec has failed.
40     virtual void SpecFailed(const ContextBase& context, const std::string& specName) = 0;
41 };

Registering a TestListener

The following version of main creates and registers a test listener with Igloo:

 1 int main()
 2 {
 3   DefaultTestResultsOutput output;
 4   TestRunner runner(output);
 5 
 6   MyTestListener listener;
 7   runner.AddListener(&listener);
 8 
 9   runner.Run();
10 }