【问题标题】:How to use gmock SaveArgPointee with with std::shared_ptr of derived class如何将 gmock SaveArgPointee 与派生类的 std::shared_ptr 一起使用
【发布时间】:2022-01-07 09:03:08
【问题描述】:

我有一个BaseMessage 类,从中派生出几个不同的DerivedMessage 子类,并希望像这样发送它们:

class BaseMessage {
public:
   virtual std::vector<uint8_t> data() const noexcept = 0;
   virtual ~BaseMessage() = default;
   [...]
}

class DerivedMessage : public BaseMessage {
public:
   [...]
   std::vector<uint8_t> data() const noexcept override { return m_data; }
private:
   std::vector<uint8_t> m_data;
}

// simplified 
class Tcp {
public
   virtual void sendMessage(std::shared_ptr<BaseMessage> msg) { write(msg->data());}
   [...]
};

class SomeClass {
public:
   SomeClass(Tcp& tcp) : m_tcp(tcp) {}
   void writeDataToRemote(std::shared_ptr<DerivedMessage> derived) const {
      m_tcp.sendMessage(derived);
private:
   Tcp m_tcp;
}
};

现在我想用 gtest 为 SomeClass 编写测试。

因此我模拟了TCP类的功能:

class MockTcp : public Tcp {
MOCK_METHOD(void, sendMessage, (std::shared_ptr<ralco::CommandMsg> msg), (override));
[...]
}

让我们假设一切都被简化到这里但可以工作。

所以在测试中,我想检查函数writeDataToRemote 中给sendMessage 的参数。 我在 StackOverflow 上阅读了有关 ::testing::SaveArg::testing::SaveArgPointee 的信息(但不在文档中)。

TEST(SomeClassTest, writesMessageToSocket){
   MockTcp mockTcp;
   SomeClass sc(mockTcp);
   
   // >>>how to declare msgArg here?<<<
   EXPECT_CALL(mockTcp, sendMessage(_)).Times(1).WillOnce(::testing::SaveArgPointee<0>(msgArg));
   const auto derivedMsg = std::make_shared<DerivedMessage>();
   sc.writeDataToRemote(derivedMsg);
   // further inspection of msgArg follows
}

正如代码注释中所写,我不知道如何声明msgArg 变量,以便将其分配给sendMessage 的实际参数。使用SaveArg 时,我想我会得到一个悬空指针并按照上面的方法执行此操作会出错,因为无法复制分配消息。任何提示都表示赞赏。

【问题讨论】:

  • 你已经有一个指向derivedMsg的指针。 SomeClass 的实际实现是否会修改指针(即更改指向的对象)?还是只修改指向对象的内容,然后进一步传递给Tcp对象?
  • 在我的实际实现中,它更像是SomeClass 中的setter 函数。我传入一个值,创建一个DerivedMessage,修改数据并将其传递给sendMessage。只是想在这里简化我的示例。
  • 我试图澄清一下,因为除非SomeClass 忽略它的输入并在将指针传递给Tcp 之前重新分配指针(即derived = std::make_shared(something_new);),否则你已经有了测试中的指针-@987654343 @指向它。您无需将其保存在任何地方。您实际上可以验证这是否与传递给模拟 (EXPECT_CALL(mockTcp, sendMessage(derivedMsg));) 的指针相同,并且您可以检查 derivedMsg 的指向对象的属性。
  • 很抱歉给您带来了困惑。也许我错误地过度简化了^^ 在我的真实场景中,我对传递给sendMessage 的消息一无所知,这就是为什么我需要将它分配给一个变量。实际上会是这样的 ` void writeDataToRemote(/* someDataVector*/ ) const { //construct derived // 将 someDataVector 设置为派生} ` 但这不是我的意思。

标签: c++ polymorphism smart-pointers gmock


【解决方案1】:

在您的情况下,您实际上只想保存并检查考虑的shared_ptr,因此使用SaveArg 就足够了:

    MockTcp mockTcp;
    SomeClass sc(mockTcp);
    std::shared_ptr<BaseMessage> bm;
    EXPECT_CALL(mockTcp, sendMessage(_)).Times(1).WillOnce(::testing::SaveArg<0>(&bm));  // it will effectively do bm = arg;
    const auto derivedMsg = std::make_shared<DerivedMessage>();
    sc.writeDataToRemote(derivedMsg);
    // verify that the argument that you captured is indeed pointing to the same message
    std::cout << derivedMsg.get() << std::endl;
    std::cout << bm.get() << std::endl;

SaveArgPointee 的常见误解是它将 arg 指向的值分配给测试中的局部变量,在您的情况下这可能不是一个好主意,因为它会调用 Message 的复制构造函数。

或者,我可以推荐使用Invoke。它非常通用且易于使用。你可以例如像这样捕获所需的参数:

    MockTcp mockTcp;
    SomeClass sc(mockTcp);
    std::shared_ptr<BaseMessage> bm;
    EXPECT_CALL(mockTcp, sendMessage(_)).Times(1).WillOnce(::testing::Invoke([&bm](auto arg) { bm = arg; }));
    const auto derivedMsg2 = std::make_shared<DerivedMessage>();
    sc.writeDataToRemote(derivedMsg2);
    std::cout << derivedMsg2.get() << std::endl;
    std::cout << bm.get() << std::endl;

或者使用指向BaseMessage的原始指针:

    BaseMessage* rawBm = nullptr;
    EXPECT_CALL(mockTcp, sendMessage(_)).Times(1).WillOnce(Invoke([&rawBm](auto arg) { rawBm = arg.get(); }));
    const auto derivedMsg2 = std::make_shared<DerivedMessage>();
    sc.writeDataToRemote(derivedMsg2);
    std::cout << derivedMsg2.get() << std::endl;
    std::cout << rawBm << std::endl;

【讨论】:

  • 这样就行了,谢谢,特别是给Invoke的提示,我会看看的。但是,我不明白为什么必须将对 shared_ptr 的引用传递给 SaveArg。你能解释一下吗?
  • 是的,因为您想更改指针的值(bm = something,而不是 *bm = something),我猜在 SaveArg 魔术背后有一些函数可以获取您的变量,所以如果你不通过&amp;bm 它将在它的副本上工作。至于 lambda 捕获中的[&amp;bm] - 同样,您希望允许 lambda 更改局部变量。
  • 好吧,这很有意义。谢谢!
猜你喜欢
  • 2016-02-24
  • 1970-01-01
  • 2015-06-21
  • 2018-02-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-04-27
  • 2022-01-23
相关资源
最近更新 更多