In very short: the issue I face is that in an
ON_CALL(...).WillByDefault(DoAll(..some capture..,Invoke([&captureValues]{..trace them...})));
the capture only works reliably if that "Invoke" is there. If I remove it, sometimes either the values are not captured correctly or they are not correctly passed on to the next expectation.
In detail: the productive code does the following:
HRESULT SomeClass::SomeMethod()
{
HRESULT hr;
int val1 = 4;
int val2 = 7;
hr = mockableObject.DoCall(val1, val2, &someOutObject);
int someOutValue = 0;
hr = someOutObject.DoOtherCall(&someOutValue);
...
return hr;
}
DoCall
returns an object, on that object DoOtherCall
is called. val1
and val2
of DoCall
shall be captured and used in the expactation of DoOtherCall
to produce the returned result.
The combination of DoCall
and DoOtherCall
on the returned object by DoCall
is executed several times in SomeClass::SomeMethod
(just if you wonder why the setup is like it is, it's the only way I found how to put these alternating calls under expectations).
The test looks as follows:
TEST_F(MySomeClassTest,
MAKE_TEST_NAME(precondition_or_setup,
tested_action,
expected_result))
{
// Arrange
SetupTestCase();
// Act
SomeClass uut;
HRESULT hr = uut.SomeMethod();
// Assert
EXPECT_EQ(hr, S_OK);
}
The setup for the test is the following:
void MySomeClassTest::SetupTestCase()
{
uint16 capturedVal1 = 0;
uint16 capturedVal2 = 0;
ON_CALL(mockedObject, DoCall(_, _, _))
.WillByDefault(
DoAll(
SaveArg<0>(&capturedVal1), // Capture value 1
SaveArg<1>(&capturedVal2), // Capture value 2
Invoke([&capturedVal1, &capturedVal2]{std::cout << "Values 1, 2: " << capturedVal1 << ", " << capturedVal2 << std::endl;}),
SetArgPointee<2>(&someOtherMock),
Return(S_OK)));
EXPECT_CALL(someOtherMock, DoOtherCall(_))
.WillRepeatedly(
Invoke([&capturedVal1, &capturedVal2](int* returnValue) {
std::cout << "Values 1, 2: " << capturedVal1 << ", " << capturedVal2 << std::endl;
*returnValue = capturedVal1 * capturedVal2;
return S_OK;
}));
}
With the Invoke
line in the ON_CALL
the tests run green.
If I remove the Invoke
line (which basically I introduced just for being able to set a breakpoint and have a trace-output), not all captured values are correct in the EXPECT_CALL
(there I trace the captured values as well).
More specifically: I know for sure DoCall
's first two arguments are always > 0. But sometimes the second argument is 0 in the EXPECT_CALL
's Invoke
.
Anybody else faced this issue? Is it a gmock bug? Do I do something wrong?
Your test code has undefined behavior. Here:
void MySomeClassTest::SetupTestCase()
{
uint16 capturedVal1 = 0;
uint16 capturedVal2 = 0;
ON_CALL(mockedObject, DoCall(_, _, _))
.WillByDefault(
DoAll(
SaveArg<0>(&capturedVal1), // Capture value 1
SaveArg<1>(&capturedVal2), // Capture value 2
Invoke([&capturedVal1, &capturedVal2]{std::cout << "Values 1, 2: " << capturedVal1 << ", " << capturedVal2 << std::endl;}),
SetArgPointee<2>(&someOtherMock),
Return(S_OK)));
You instruct mockObject
by SaveArg<0>(&capturedVal1)
SaveArg<1>(&capturedVal2)
to store data in local variables capturedVal1
and capturedVal2
which lifetime ends at the time when mocked method is invoked.
Move capturedVal1
and capturedVal2
to be a class fields.
Final note:
I no longer use SaveArg<0>
and couple other gtest APIs. They are extremely useful when using C++03, but since C++11 it can be replaced by lambda, which is IMHO more handy.
Anyway Why you need those variables at all? You do not use it to anything meaningfull accept printing (which bad). So you can just do this:
ON_CALL(mockedObject, DoCall(_, _, _))
.WillByDefault(Invoke([] (auto a, auto b, auto c) {
std::cout << "Values 1, 2: " << a << ", " << b << std::endl;
return S_OK;
})
You should not print anything to std::cout
. This makes test very annoying, noisy and there other ways to print values in gtest, so those things are printed only when test fails.