eranpeer / FakeIt

C++ mocking made easy. A simple yet very expressive, headers only library for c++ mocking.
MIT License
1.22k stars 169 forks source link

No approximate matchers #218

Closed mbsmith93 closed 2 years ago

mbsmith93 commented 3 years ago

One very useful feature that is missing from FakeIt is approximate matchers. This is an important feature for anyone doing floating point equality comparisons; in practice floating point comparisons are rarely equal, but this usually isn't important to test writers. As additional evidence of the importance of approximate matchers, see documentation on the subject from the boost test library: https://www.boost.org/doc/libs/1_74_0/libs/test/doc/html/boost_test/testing_tools/extended_comparison/floating_point.html

For my own work I slapped together a "Close" matcher that provides this functionality. There are definitely some formatting and namespacing issues that make it not quite right in its current form, but it would be fairly easy to move things around and put this into the "argument_matchers.hpp" file. Here's what I have:

template <typename T>
class CloseMatcherCreator : public fakeit::TypedMatcherCreator<T> {
  public:
    CloseMatcherCreator(const T& expected, const T& tolerance)
            : expected(expected), tolerance(tolerance) {}

    virtual ~CloseMatcherCreator() = default;

    class Matcher : public fakeit::TypedMatcher<T> {
      public:
        Matcher(const T& expected, const T& tolerance)
                : expected(expected), tolerance(tolerance) {}

        virtual bool matches(const T& actual) const override {
            return (actual >= expected - tolerance)
                   && (actual <= expected + tolerance);
        }

        virtual std::string format() const override {
            return std::string("Approximately ")
                   + fakeit::TypeFormatter<T>::format(expected);
        }

        T expected;
        T tolerance;
    };

    virtual fakeit::TypedMatcher<T>* createMatcher() const override {
        return new Matcher(expected, tolerance);
    }

    T expected;
    T tolerance;
};

template <typename T>
CloseMatcherCreator<T> Close(const T& expected, const T& tolerance) {
    CloseMatcherCreator<T> close(expected, tolerance);
    return close;
}

I'd be happy to make this change myself, but the documentation for developers contributing to FakeIt is a little spotty - what do I need to do to generate all of the "single_header" hpp targets, and is there anything else I need to do for this to all work right?

teeks99 commented 3 years ago

I support this solution. Just for posterity, I wanted to document a workaround to this problem, should others end up here before such a change is merged in:

class MyInterface
{
   void MyMethod(double input) = 0;
};

BOOST_AUTO_TEST_CASE(test)
{
    double expected = 0.123;
    double tolerance = 0.001;

    double input = 0.123000000001;

    auto isClose = boost::math::fpc::close_at_tolerance<double>(tolerance);

    Mock<MyInterface> mock;
    Fake(Method(mock, MyMethod));

    mock.Method(input);

    Verify(Method(mock, MyMethod).Matching([isClose, expected](float input) {return isClose(expected, input);} )).Exactly(1);
}
FranckRJ commented 2 years ago

Added in #271.