Download News Support Project

PrevUpHomeNext

Patterns

Managing static mock objects
Waiting for an asynchronous call
Retrieving an argument to use in a later constraint
Invoking a functor received as parameter
Quickly writing a custom constraint for a serializable type

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));
}


PrevUpHomeNext