eranpeer / FakeIt

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

Mocking methods with params as references result with storing those references regardles of their actual lifetime. #108

Closed parzysty closed 2 years ago

parzysty commented 6 years ago

For the following scenario:

class HelperClass {
public:
    virtual void usefullMethod(const int& x, const int& y) {
    }
};

class TestedClass {
public:
    TestedClass(HelperClass& other)
        : other(other)
    {}
    virtual void testMethod() {
        other.usefullMethod(10, 20);
    }
private:
        HelperClass& other;
};

Mock<HelperClass> fakeClass;
TestedClass underTest(fakeClass.get());
Fake(Method(fakeClass, usefullMethod));

underTest.testMethod();

Verify(Method(fakeClass, usefullMethod).Using(10, 20)).Once();

test will most likely fail, as (my guess) mocked class is keeping references to passed arguments and when Verify is called referenced values are gone.

otterovich commented 6 years ago

Please see the latest answer here. This is a simple workaround which helped me in this particular case.

mmatthe commented 5 years ago

The Proposed workaround does not work fully. In case you made the modification to fakeit.hpp, you cannot mock any functions that take const-ref arguments using "When". See eg this code:

struct S{
  int a;
  int b;
};

class C2 {
public:
  virtual int callWithConstRef(const S& s) = 0;
};

TEST_CASE("Fakeit does not compile with const ref args?") {
  using namespace fakeit;
  Mock<C2> mock;
  When(Method(mock, callWithConstRef)).Return(2);
  REQUIRE(mock.get().callWithConstRef(S{2,3}) == 2);
}

Error message when compiling with gcc7

/tests/fakeit.hpp: In instantiation of ‘R fakeit::Repeat<R, arglist>::invoke(fakeit::ArgumentsTuple<arglist ...>&) [with R = int; arglist = {const S&}; fakeit::ArgumentsTuple<arglist ...> = std::tuple<const S>]’:
/tests/test_fakeit.cpp:57:1:   required from here
/tests/fakeit.hpp:7095:58: error: no matching function for call to ‘fakeit::TupleDispatcher::invoke<int, const S&>(std::function<int(const S&)>&, fakeit::ArgumentsTuple<const S&>&)’
             return TupleDispatcher::invoke<R, arglist...>(f, args);
                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~
/tests/fakeit.hpp:6211:18: note: candidate: template<class R, class ... arglist> static R fakeit::TupleDispatcher::invoke(std::function<R(ArgsF& ...)>, const std::tuple<_Elements ...>&)
         static R invoke(std::function<R(arglist &...)> func, const std::tuple<arglist...> &arguments) {
                  ^~~~~~
/tests/fakeit.hpp:6211:18: note:   template argument deduction/substitution failed:
/tests/fakeit.hpp:7095:58: note:   mismatched types ‘const S&’ and ‘const S’
             return TupleDispatcher::invoke<R, arglist...>(f, args);
                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~
/tests/fakeit.hpp: In instantiation of ‘R fakeit::RepeatForever<R, arglist>::invoke(fakeit::ArgumentsTuple<arglist ...>&) [with R = int; arglist = {const S&}; fakeit::ArgumentsTuple<arglist ...> = std::tuple<const S>]’:
/tests/test_fakeit.cpp:57:1:   required from here
/tests/fakeit.hpp:7117:58: error: no matching function for call to ‘fakeit::TupleDispatcher::invoke<int, const S&>(std::function<int(const S&)>&, fakeit::ArgumentsTuple<const S&>&)’
             return TupleDispatcher::invoke<R, arglist...>(f, args);
                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~
/tests/fakeit.hpp:6211:18: note: candidate: template<class R, class ... arglist> static R fakeit::TupleDispatcher::invoke(std::function<R(ArgsF& ...)>, const std::tuple<_Elements ...>&)
         static R invoke(std::function<R(arglist &...)> func, const std::tuple<arglist...> &arguments) {
                  ^~~~~~
/tests/fakeit.hpp:6211:18: note:   template argument deduction/substitution failed:
/tests/fakeit.hpp:7117:58: note:   mismatched types ‘const S&’ and ‘const S’
             return TupleDispatcher::invoke<R, arglist...>(f, args);
                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~

Compilation exited abnormally with code 1 at Mon Mar 11 07:43:59

(see the mismatched types const S& and const S line.

I believe this is due to the std::remove_reference proposal. Any workarounds?

osmeest commented 4 years ago

I second this request. Being able to verify invocations with reference arguments is an absolute must. Setting a kind of "recorder" action is too cumbersome. But it seems like a fundamental issue with the way fakeit Verifications are made. fakeit records the calls and checks after the fact. This implies that arguments to the recorded calls have to be copied. Note that argument checking works for the When/Fake side, because those are registered upfront. Comparison of the ref arguments is then made against what is stored in the Using() call.

malcolmdavey commented 3 years ago

yes - this seems like a big flaw. Especially if it doesn't work for string classes.

If it can detect something has a copy constructor, wonder if it can use to implement the copying.

Or if it can use a matcher object which can do the copying itself

FranckRJ commented 2 years ago

I'll centralize the discussion about this known bug in a new issue: #274