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:

class person
{
public:
	// Set the Person's name.
	void name(const std::string & name)
	{
		name_ = name;
		updated(*this);
	};
	// An signal that will be called when the person's details are updated.
	boost::signal<void(const person & person)> updated;
private:
	// The person's name.
	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:

struct mock_handler
{
	mock_handler(const person & expected_person) :
		has_been_called_(false), expected_person_(expected_person) {};
	// The signal handler function.
	void operator()(const person & person)
	{
		has_been_called_ = true;
		BOOST_CHECK_EQUAL(&person == &expected_person_, true);
	};
	// This handler must be called before it goes out of scope.
	~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.

// Test that setting a new name triggers the person.updated signal.
BOOST_AUTO_TEST_CASE(setting_name_triggers_update_signal)
{
	person subject;
	mock_handler handler(subject);
	subject.updated.connect(boost::ref(handler));
	// Change the person's name, triggering the updated signal.
	subject.name("Richard");
}

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

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.