如何将 gmock SaveArgPointee 与派生 class 的 std::shared_ptr 一起使用
How to use gmock SaveArgPointee with with std::shared_ptr of derived class
我有一个 BaseMessage
class,我从中派生了几个不同的 DerivedMessage
子 classes 并想像这样发送它们:
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:
class MockTcp : public Tcp {
MOCK_METHOD(void, sendMessage, (std::shared_ptr<ralco::CommandMsg> msg), (override));
[...]
}
让我们假设到这里为止所有内容都已简化但有效。
所以在测试中,我想检查函数writeDataToRemote
中给sendMessage
的参数。
我在 Whosebug 上读到了 ::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
时,我想我会得到一个悬空指针并按照上面的方式进行操作时出现错误,因为无法复制分配消息。感谢任何提示。
在您的情况下,您实际上只想保存并检查所考虑的 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;
我有一个 BaseMessage
class,我从中派生了几个不同的 DerivedMessage
子 classes 并想像这样发送它们:
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:
class MockTcp : public Tcp {
MOCK_METHOD(void, sendMessage, (std::shared_ptr<ralco::CommandMsg> msg), (override));
[...]
}
让我们假设到这里为止所有内容都已简化但有效。
所以在测试中,我想检查函数writeDataToRemote
中给sendMessage
的参数。
我在 Whosebug 上读到了 ::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
时,我想我会得到一个悬空指针并按照上面的方式进行操作时出现错误,因为无法复制分配消息。感谢任何提示。
在您的情况下,您实际上只想保存并检查所考虑的 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;