eranpeer / FakeIt

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

(Always)Return doesn't seem to allow capturing locals by reference? #200

Open CelticMinstrel opened 4 years ago

CelticMinstrel commented 4 years ago

The following minimal example demonstrates the issue:

#include <vector>
#include <string>

#include "catch.hpp"
#include "fakeit.hpp"

using namespace std;
using namespace fakeit;

struct IExample {
    virtual vector<string> getList() const = 0;
};

TEST_CASE("Mock capture by reference") {
    Mock<IExample> mock;
    vector<string> values;
    When(Method(mock, getList)).AlwaysReturn(ref(values));
    IExample& x = mock.get();
    CHECK(x.getList().size() == 0);
    values.push_back("Hello");
    values.push_back("World");
    REQUIRE(x.getList().size() == 2); // This assertion fails
    CHECK(x.getList()[0] == "Hello");
    CHECK(x.getList()[1] == "World");
}

I can see that (Always)Return has reference and non-reference overloads, maybe adding a reference_wrapper overload would help?

For now I've worked around it by directly using (Always)Do instead.

malcolmdavey commented 2 years ago

Interesting. I was trying to fix the opposite case. If it is returning a reference, or const reference, then we need to make sure it is long lived - so want to force it to copy it( adding a "ReturnCopy", which takes a copy of a reference)

I was able to add an alternative which took a copy, and then extended the life time, so that the return worked for references. It's likely that something might be able to work for this too.

Hence adding a "ReturnRef" method might work.

malcolmdavey commented 2 years ago

Actually there is a trick which already works. You can pass the type as a reference to the function like this AlwaysReturn<vector<string>&>.

TEST_CASE("Mock capture by reference") {
    Mock<IExample> mock;
    vector<string> values;
    When(Method(mock, getList)).AlwaysReturn<vector<string>&>(ref(values));
    IExample& x = mock.get();
    CHECK(x.getList().size() == 0);
    values.push_back("Hello");
    values.push_back("World");
    REQUIRE(x.getList().size() == 2); // This assertion fails
    CHECK(x.getList()[0] == "Hello");
    CHECK(x.getList()[1] == "World");
}