Download | News | Support | Project |
This section highlights not-so-obvious features of the library gathered from real use cases.
Problem :
#include <turtle/mock.hpp> #include <boost/test/unit_test.hpp> #include <ostream> namespace { struct my_class { my_class(int i) : i_(i) {} int i_; }; std::ostream& operator<<(std::ostream& os, const my_class* c) { return os << "my_class " << c->i_; // the 'c' pointer must be valid when logging } MOCK_FUNCTION(f, 1, void(my_class*)) // being static 'f' outlives the test case } // namespace BOOST_AUTO_TEST_CASE(static_objects_problem) { my_class c(42); MOCK_EXPECT(f).once().with( &c); // the set expectation will also outlive the test case and leak into other test cases using 'f' } // the 'c' instance goes out of scope and the '&c' pointer becomes dangling
Partial solution :
struct fixture { ~fixture() { mock::reset(); // the use of a fixture ensures the reset will prevent the expectations from leaking into other // test cases } }; BOOST_FIXTURE_TEST_CASE(static_object_partial_solution, fixture) { my_class c(42); MOCK_EXPECT(f).once().with(&c); f(&c); mock::verify(); // verify the expectations before local objects are destroyed and before the fixture resets them }
Solution :
BOOST_FIXTURE_TEST_CASE( static_objects_solution, mock::cleanup) // actually the library includes a ready to use fixture just like the one described { my_class c(42); MOCK_EXPECT(f).once().with(&c); f(&c); mock::verify(); }
Problem :
namespace mock_test { class base_class { public: virtual void method() = 0; }; class my_class { base_class& b; public: explicit my_class(base_class&); void flush(); // repetitively calling this method will in turn call base_class::method at some point }; } // namespace mock_test
Solution :
#include <turtle/mock.hpp> #include <boost/test/unit_test.hpp> #include <boost/thread.hpp> namespace mock_test { template<typename F> void check(bool& condition, F flush, int attempts = 100, int sleep = 100) { while(!condition && attempts > 0) { --attempts; boost::this_thread::sleep(boost::posix_time::milliseconds(sleep)); flush(); } } MOCK_BASE_CLASS(mock_base_class, base_class) { MOCK_METHOD(method, 0) }; BOOST_AUTO_TEST_CASE(method_is_called) { mock_base_class m; my_class c(m); bool done = false; MOCK_EXPECT(m.method).once().calls([&done]() { done = true; }); check(done, [&c]() { c.flush(); }); // just wait on done, flushing from time to time } } // namespace mock_test
Problem :
namespace { class base_class { public: virtual void method(int value) = 0; }; class my_class { public: explicit my_class(base_class&); void process(); // the processing will call 'method' two times with the same value, but we don't know what value // beforehand }; } // namespace
Solution :
#include <turtle/mock.hpp> #include <boost/test/unit_test.hpp> namespace { MOCK_BASE_CLASS(mock_base_class, base_class) { MOCK_METHOD(method, 1) }; } // namespace BOOST_AUTO_TEST_CASE(method_is_called_two_times_with_the_same_value) { mock_base_class mock; my_class c(mock); int value; MOCK_EXPECT(mock.method).once().with(mock::retrieve(value)); // on first call retrieve the value, this expectation // takes precedence because it can never fail MOCK_EXPECT(mock.method) .once() .with(std::cref(value)); // on second call compare the previously retrieved value with the newly received one c.process(); }
Problem :
#include <functional> class base_class { public: virtual void method(const std::function<void(int)>& functor) = 0; }; // the function will call 'method' with a functor to be applied void function(base_class& c) { c.method(someFunctor); }
Solution :
#include <turtle/mock.hpp> #include <boost/test/unit_test.hpp> namespace { MOCK_BASE_CLASS(mock_class, base_class) { MOCK_METHOD(method, 1) }; } // namespace BOOST_AUTO_TEST_CASE(how_to_invoke_a_functor_passed_as_parameter_of_a_mock_method) { mock_class mock; MOCK_EXPECT(mock.method).calls([](const auto& functor) { functor(42); }); // whenever 'method' is called, invoke the functor with 42 function(mock); }
Problem :
#include <turtle/mock.hpp> #include <boost/test/unit_test.hpp> #include <iostream> namespace { class my_class { public: explicit my_class(int data) : data_(data) {} int data_; }; std::ostream& operator<<(std::ostream& os, const my_class& c) // my_class is serializable to an std::ostream { return os << "my_class( " << c.data_ << " )"; } MOCK_CLASS(my_mock) { MOCK_METHOD(method, 1, void(const my_class&)) // how to simply write a custom constraint ? }; } // namespace
Solution :
#include <sstream> namespace // in the same namespace as 'my_class' { bool operator==(const my_class& actual, const std::string& expected) // the first part of the trick is to compare to a string { std::ostringstream s; s << actual; return s.str() == expected; } } // namespace BOOST_AUTO_TEST_CASE(method_is_called) { my_mock mock; MOCK_EXPECT(mock.method) .once() .with("my_class( 42 )"); // the second part of the trick is to express the constraint as a string mock.method(my_class(42)); }