Using google test and mocks it seems that I cannot delegate a call from a mock to a fake if the function returns a reference to a data object. The google test version I'm using is 1.10.0 from the released zip.
In the code below when I delegate from a mock to a fake I get an error indicating that the copy ctor is deleted. Yes, it must be deleted for this code to work properly.
Is there any way to delegate mocks to fakes with gmock for functions which return references to classes?
Note that in the code below, there is a macro:
#define USE_MOCK_ACCESSOR 1
This is used to test the desired path of test code execution.
Defining this value to zero merely tests the AccessorImpl class for correct behavior. I did this to check that I was not somehow malforming the classes and instances within this class.
Thanks for your input.
#include "gtest/gtest.h"
#include "gmock/gmock.h"
#include <cstdint>
using ::testing::Invoke;
using ::testing::Mock;
using ::testing::Return;
using ::testing::ReturnRef;
using ::testing::_;
class Accessor
{
public:
virtual ~Accessor() = default;
Accessor() = default;
Accessor(Accessor const&) = delete;
Accessor(Accessor&&) = delete;
Accessor& operator=(Accessor const&) = delete;
Accessor& operator=(Accessor&&) = delete;
struct Foo
{
~Foo() = default;
Foo() = default;
Foo(Foo const&) = delete;
Foo(Foo&&) = delete;
Foo& operator=(Foo const&) = delete;
Foo& operator=(Foo&&) = delete;
uint32_t thing_1 = 13u;
};
struct Bar
{
~Bar() = default;
Bar() = default;
Bar(Bar const&) = delete;
Bar(Bar&&) = delete;
Bar& operator=(Bar const&) = delete;
Bar& operator=(Bar&&) = delete;
uint32_t thing_2 = 79u;
};
virtual Foo& GetFoo() = 0;
virtual Bar const& GetBar() const = 0;
};
class AccessorImpl: public Accessor
{
public:
~AccessorImpl() override = default;
AccessorImpl() = default;
AccessorImpl(AccessorImpl const& ) = delete;
AccessorImpl(AccessorImpl&&) = delete;
AccessorImpl& operator=(AccessorImpl const&) = delete;
AccessorImpl& operator=(AccessorImpl&&) = delete;
Foo& GetFoo() override { return this->foo_; };
Bar const& GetBar() const override { return this->bar_; };
private:
Foo foo_;
Bar bar_;
};
#define USE_MOCK_ACCESSOR 1
#if USE_MOCK_ACCESSOR
class MockAccessor : public Accessor
{
public:
MOCK_METHOD0(GetFoo, Foo&());
MOCK_CONST_METHOD0(GetBar, Bar&());
};
class MockAccessorWithFake : public MockAccessor
{
public:
MockAccessorWithFake() : MockAccessor(), fake_accessor_()
{
ON_CALL(*this, GetFoo).WillByDefault([this]() {
return this->fake_accessor_.GetFoo();
});
ON_CALL(*this, GetBar).WillByDefault([this]() {
return this->fake_accessor_.GetBar();
});
}
private:
AccessorImpl fake_accessor_;
};
#endif
TEST(AccessorTest, test)
{
#if USE_MOCK_ACCESSOR
MockAccessorWithFake accessor;
#else
AccessorImpl accessor;
#endif
EXPECT_EQ(accessor.GetFoo().thing_1, 13u);
EXPECT_EQ(accessor.GetBar().thing_2, 79u);
}
Errors from clang compiler:
test_accessor.cc:83:20: error: call to deleted constructor of 'Accessor::Foo'
return this->fake_accessor_.GetFoo();
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
test_accessor.cc:26:9: note: 'Foo' has been explicitly marked deleted here
Foo(Foo const&) = delete;
^
test_accessor.cc:82:46: error: no viable conversion from '(lambda at
test_accessor.cc:82:46)' to 'const Action<Accessor::Foo &()>'
ON_CALL(*this, GetFoo).WillByDefault([this]() {
^~~~~~~~~~
googletest-src/googlemock/include/gmock/gmock-actions.h:339:7: note: candidate constructor (the implicit copy constructor) not viable:
no known conversion from '(lambda at test_accessor.cc:82:46)' to 'const testing::Action<Accessor::Foo &()> &' for 1st argument
class Action {
^
googletest-src/googlemock/include/gmock/gmock-actions.h:339:7: note: candidate constructor (the implicit move constructor) not viable:
no known conversion from '(lambda at test_accessor.cc:82:46)' to 'testing::Action<Accessor::Foo &()> &&' for 1st argument
googletest-src/googlemock/include/gmock/gmock-actions.h:367:3: note: candidate template ignored: requirement
'::std::is_constructible<std::__1::function<Accessor::Foo &()>, (lambda at test_accessor.cc:82:46)>::value' was not satisfied
[with G = (lambda at test_accessor.cc:82:46)]
Action(G&& fun) : fun_(::std::forward<G>(fun)) {} // NOLINT
^
googletest-src/googlemock/include/gmock/gmock-spec-builders.h:323:46: note: passing argument to parameter 'action' here
OnCallSpec& WillByDefault(const Action<F>& action) {
The rules for auto
type deduction strip references. Therefore, the return type of your lambda is deduced to be Foo
instead of Foo&
which then requires a copy. If you want to return a reference from a lambda, you have to specify this explicitly using the trailing return type syntax, either by explicitly setting the return type to Foo&
, by using auto&
to force a reference type deduction, or by using decltype(auto)
, which preserves references. See link, link, link, in the last link the relevant part is: "If P is a reference type, the type referred to by P is used for deduction."
[this]() {return this->fake_accessor_.GetFoo();} // Returns Foo
[this]() -> Foo& {return this->fake_accessor_.GetFoo();} // Returns Foo&
[this]() -> auto {return this->fake_accessor_.GetFoo();} // Returns Foo
[this]() -> auto& {return this->fake_accessor_.GetFoo();} // Returns Foo&
[this]() -> decltype(auto) {return this->fake_accessor_.GetFoo();} // Returns Foo&
So you should change the lambdas you pass to ON_CALL
to return a reference type, e.g.:
ON_CALL(*this, GetFoo).WillByDefault([this]() -> Foo& {
return this->fake_accessor_.GetFoo();
});
ON_CALL(*this, GetBar).WillByDefault([this]() -> Bar const& {
return this->fake_accessor_.GetBar();
});
Without this, you get the errors:
test.cpp:83:20: error: call to deleted constructor of 'Accessor::Foo'
return this->fake_accessor_.GetFoo();
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
test.cpp:26:9: note: 'Foo' has been explicitly marked deleted here
Foo(Foo const&) = delete;
^
test.cpp:82:46: error: no viable conversion from '(lambda at test.cpp:82:46)' to 'const Action<Accessor::Foo &()>'
ON_CALL(*this, GetFoo).WillByDefault([this]() {
^~~~~~~~~~
In your declaration of GetBar
, you have two usages of const
:
const
member function (meaning it cannot modify the state of this
).const Bar
.The MOCK_CONST_METHOD0
macro only declares const
member function. To cover the const
in the return value, your mock should be:
MOCK_CONST_METHOD0(GetBar, Bar const&());
Without this change, the following error will be generated:
test.cpp:86:46: error: no viable conversion from '(lambda at test.cpp:86:46)' to 'const Action<Accessor::Bar &()>'
ON_CALL(*this, GetBar).WillByDefault([this]() -> Bar const& {
^~~~~~~~~~~~~~~~~~~~~~~~
/usr/include/gmock/gmock-actions.h:357:7: note: candidate constructor (the implicit copy constructor) not viable: no known conversion from '(lambda at test.cpp:86:46)' to 'const testing::Action<Accessor::Bar &()> &' for 1st argument
class Action {