google 假的模拟委托必须是可复制构造的

google mock delegate to fake must be copy constructable

使用 google 测试和模拟,如果函数 return 是对数据对象的引用,我似乎无法将调用从模拟委托给伪造。我使用的 google 测试版本是发布的 zip 中的 1.10.0。

在下面的代码中,当我从一个 mock 委托给一个 fake 时,我收到一条错误消息,指出复制 ctor 已被删除。是的,必须删除它才能使此代码正常工作。

对于 return 引用 classes 的函数,是否有任何方法可以使用 gmock 将模拟委托给伪造?

请注意,在下面的代码中,有一个宏: #define USE_MOCK_ACCESSOR 1 这用于测试测试代码执行的所需路径。 将此值定义为零仅测试 AccessorImpl class 的正确行为。我这样做是为了检查我没有以某种方式使 class 中的 classes 和实例变形。 感谢您的输入。

#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);
}

来自 clang 编译器的错误:

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) {

问题 1

auto 类型扣除带引用的规则。因此,您的 lambda 的 return 类型被推断为 Foo 而不是 Foo& 然后需要一个副本。如果你想 return 来自 lambda 的引用,你必须使用尾随 return 类型语法明确指定,或者通过将 return 类型显式设置为 Foo&,通过使用 auto& 强制引用类型推导,或使用 decltype(auto) 来保留引用。见link, link, link,最后link相关部分是:“如果P是引用类型,则P所引用的类型用于推导。”

[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&

因此您应该将传递给 ON_CALL 的 lambda 更改为 return 引用类型,例如:

        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();
        });

没有这个,你会得到错误:

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]() {
                                             ^~~~~~~~~~

问题 2

GetBar 的声明中,const 有两种用法:

  1. 该函数是一个const成员函数(意味着它不能修改this的状态)。
  2. return 值是对 const Bar 的引用。

MOCK_CONST_METHOD0宏只声明了const成员函数。要覆盖 return 值中的 const,您的 mock 应该是:

  MOCK_CONST_METHOD0(GetBar, Bar const&());

如果不进行此更改,将生成以下错误:

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 {