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

Cannot setup Return as separate statement #202

Open Stannieman opened 4 years ago

Stannieman commented 4 years ago
class IHelper
{
public:
    virtual ~IHelper() = default;
    virtual int MyMethod() const = 0;
}

int main()
{
    Mock<IHelper> mock;
    auto &setup = When(Method(mock, MyMethod));
    setup.Return(1);

    return 0;
}

In this example "setup.Return(1);" crashes with read access violation.

It would be quite useful to allow calling Return as a separate statement. Then you can have multiple return values in a vector and do:

vector<int> returnValues{ 1, 2, 3 };
for (const auto returnValue : returnValues)
{
    setup.Return(returnValue );
}

Currently I have to resort to:

size_t index = 0;
vector<int> returnValues{ 1, 2, 3 };
When(Method(mock, MyMethod))
   .AlwaysDo([&]() { return returnValues[index++]; }

I usually set up my mocks in helper methods to share setup code between tests, so that then becomes:

void SetupMyMethod(Mock<IHelper> &mock, const vector<int> &returnValues, size_t &index)
{
    When(Method(mock, MyMethod))
       .AlwaysDo([&]() { return returnValues[index++]; }
}

as you can see there is an additional index reference parameter that you need to keep track of, which is not convenient.

Stannieman commented 4 years ago

Ok you can do this:

void SetupMyMethod(Mock<IHelper> &mock, const vector<int> &returnValues)
{
    const auto index = std::make_shared<std::size_t>();
    When(Method(mock, MyMethod))
       .AlwaysDo([&, index]() { return returnValues[(*index)++]; }
}

No reference index no more. So this makes this issue a lot less annoying. Still a nice to have though, but no priority at the moment.

dmeehan1968 commented 3 years ago

I have a similar problem but need to programmatically build the return values from different sources. Sadly it seems that Fakeit has implemented the MethodStubbingProgress return values as temporaries which means they cannot be stored and used over multiple calls. Also, the When() call establishes a new chain of return values, rather than adding to those already existing.

The solution proposed in the last comment really only works if all the return values are known at the time the When() call is made, which doesn't work for my use case. As a result, I'm left with having to build my own array of return values which ultimately means I might as well write my own mocks by subclassing. At least from what I can see of the available API, unless someone else can enlighten me.

Ideally I'd like an API that does (NB: this is over simplified, in reality the progress calls are spread over different parts of the test setup (I'm using Cucumber step_definitions, each providing additional values to describe a sequence).

auto progress = When(Method(mock, myMethod)); // establishes a new return chain
progress.Return(value);
progress.Return(value);
progress.Return(value); // repeat as required
malcolmdavey commented 2 years ago

Wondering if a new chain is ever really useful?

malcolmdavey commented 2 years ago

In general you shouldn't use auto& for return by value. And auto&& is useful here if you don't know the type, but seems this doesn't seem to work either, and there is some subtle logic somewhere that depends on it being in the same statement.

My guess is it's a deep subtle flaw. Might have a deep look if/when I have time.