Spread the love















Today, while writing some unit tests, I encountered a challenge. The user story was that, when a Person’s details are updated, the display should be updated to reflect the changes.

I’d implemented this feature using a signal on the person class that will be called whenever any details are updated:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class person { public : void name( const std::string & name) { name_ = name; updated(* this ); }; boost:: signal < void ( const person & person)> updated; private : std::string name_; };

This is a fairly standard application of an observer pattern that you might find in any MVC application.

But the question is, using the Boost unit test framework, how can I test if my signal has been called?

The mock signal handler

To test the signal handler, we’ll use a functor as a mock signal handler, that sets an internal flag when it gets called. In the functor’s destructor, we’ll do a test on the flag to make sure it got set:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 struct mock_handler { mock_handler( const person & expected_person) : has_been_called_( false ), expected_person_(expected_person) {}; void operator()( const person & person) { has_been_called_ = true ; BOOST_CHECK_EQUAL(&person == &expected_person_, true ); }; ~mock_handler() { BOOST_CHECK_EQUAL(has_been_called_, true ); }; private : bool has_been_called_; const person & expected_person_; };

The test case

Once we’ve written a mock, the test case is pretty simple. Note that I wrap my handler with a boost::ref, so that it doesn’t get copied.

1 2 3 4 5 6 7 8 9 10 11 BOOST_AUTO_TEST_CASE(setting_name_triggers_update_signal) { person subject; mock_handler handler(subject); subject.updated.connect(boost::ref(handler)); subject.name( "Richard" ); }

This works great. And if we comment out the updated signal call in person::name():

1 2 3 Running 1 test case... person_test.cpp(49): error in "setting_name_triggers_update_signal": check has_been_called_ == true failed [0 != 1] *** 1 failure detected in test suite "tests"

..then the test case will fail accordingly.