【问题标题】:How to call EXPECT_CALL gtest macro before the object construction如何在对象构造之前调用 EXPECT_CALL gtest 宏
【发布时间】:2022-11-21 02:39:33
【问题描述】:

我有一个在初始化列表中调用模拟函数的类。 我想使用 EXPECT_CALL 来验证模拟函数是否只被调用一次。 问题是我不能在构造函数之前使用宏,因为它是第一个运行的函数,也不能在它之后使用,因为在构造函数中调用了模拟函数。

例如:用户界面.cpp

class UI {
  public:
    UI() = default;
    ~UI() = default;

    virtual std::string get_name() {
        std::string name;
        std::cin >> name;
        return name;
    }
};

foo.cpp文件

class Foo {
  public:
    Foo(UI ui) : m_name(ui.get_name()) {}
    ~Foo() = default;
};

mock_ui.hpp

class MockUI : public UI {
    MockUI() : UI() = default;
    ~MockUI() = default;

    MOCK_METHOD(std::string, get_name, (), (override));
};

问题出现在这里:foo_test.cpp

class FooTest : ::testing::Test {
  public:
    // I want to call EXPECT_CALL(m_ui, get_name()) before this line executes.
    FooTest() : m_foo(MockUI()) {}
    ~FooTest() = default;

  protected:
    void SetUp() override {}
    void TearDown() override {}

    Foo m_foo;
    MockUI m_ui;
};

我尝试在 SetUp() 函数中初始化 Foo 对象,但是 Foo 没有默认构造函数,所以它必须在 FooTest 构造函数中初始化。

解决方案?我唯一的想法是在 MockUI 构造函数中调用 EXPECT_CALL(),如下所示:mock_ui.hpp

class MockUI : public UI {
    MockUI() : UI() {
        EXPECT_CALL(*this, get_name());
    }
    ~MockUI() = default;
    
    MOCK_METHOD(std::string, get_name, (), (override);
};

问题是我可能会在不调用 get_name() 或多次调用它的情况下使用 MockUI,但这是我拥有的最佳解决方案。

还有其他建议吗?

【问题讨论】:

  • 不管构造函数中EXPECT_CALL 的可能性如何,显示的代码都设计得不好。对象不应该关心它的方法是否被调用。
  • 你能提供MCVE吗?这是godbolt starting point。什么是被测代码?
  • 我还看到您以错误的方式注入依赖项(按值传递)。

标签: c++ googletest gmock


【解决方案1】:

您可以使用指针延迟 Foo 的初始化:

class FooTest : ::testing::Test {
public:
    FooTest() 
    { 
        EXPECT_CALL(m_ui, get_name()); 
        m_foo = std::make_unique<Foo>(m_ui); 
    }
protected:
    std::unique_ptr<Foo> m_foo;
    MockUI m_ui;
};

或者通过添加一个将首先初始化 MockUI 的父类:

class FooTestHelper: public ::testing::Test
{
public:
    FooTestHelper() { EXPECT_CALL(m_ui, get_name()); }
protected:
    MockUI m_ui;
};

class FooTest : public FooTestHelper
{
public:
    FooTest(): FooTestHelper{}, m_foo{m_ui} {}
private:
    Foo m_foo;
};

此外,正如评论者所注意到的,UI 按值传递给 Foo,这意味着不会发生继承。

【讨论】:

    【解决方案2】:

    您还可以在虚拟变量的初始化中添加 EXPECT_CALL:

    class FooTest : ::testing::Test {
      public:
        FooTest() {}
        ~FooTest() = default;
    
      protected:
        void SetUp() override {}
        void TearDown() override {}
    
        MockUI m_ui;
        bool dummy = (EXPECT_CALL(m_ui, get_name()), true);
        Foo m_foo{m_ui};
    };
    

    我移动了 m_foo 的初始化,但它只是为了更好地了解初始化顺序,您可以将其留在构造函数中。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-03-31
      • 1970-01-01
      • 1970-01-01
      • 2015-07-01
      • 2010-10-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多