гугл макет делегат на подделку должен быть копируемый - PullRequest
2 голосов
/ 31 марта 2020

При использовании google test и mocks кажется, что я не могу делегировать вызов из mock для подделки, если функция возвращает ссылку на объект данных. Тестовая версия Google я использую 1.10.0 из выпущенного почтового индекса.

В коде ниже, когда я делегирую из макета на подделку, я получаю сообщение об ошибке, указывающее, что копия ctor удалена. Да, он должен быть удален, чтобы этот код работал должным образом.

Есть ли способ делегировать макеты подделкам с помощью gmock для функций, которые возвращают ссылки на классы?

Обратите внимание, что в приведенном ниже коде есть макрос: #define USE_MOCK_ACCESSOR 1 Используется для проверки желаемого пути выполнения тестового кода. Определение этого значения в ноль просто проверяет класс AccessorImpl на правильное поведение. Я сделал это, чтобы убедиться, что я не искажал классы и экземпляры в этом классе. Спасибо за ваш вклад.

#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 Ответ

2 голосов
/ 17 апреля 2020

Задача 1

Правила для auto ссылок на типовые вычеты. Следовательно, тип возврата вашей лямбды выводится как Foo вместо Foo&, который затем требует копию. Если вы хотите вернуть ссылку из лямбды, вы должны указать это явно, используя конечный синтаксис возвращаемого типа, либо явно указав тип возвращаемого значения Foo&, используя auto& для принудительного вывода ссылочного типа, либо используя decltype(auto), который сохраняет ссылки. См. ссылка , ссылка , ссылка , в последней ссылке соответствующая часть: «Если 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, чтобы вернуть тип ссылки, например:

        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. Возвращаемое значение является ссылкой на const Bar.

Макрос MOCK_CONST_METHOD0 только объявляет const функция-член. Чтобы покрыть const в возвращаемом значении, ваш макет должен быть:

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