使用 GoogleMock 模拟由被测代码创建的实例
Using GoogleMock to mock an instance created by code under test
我已经创建了一个界面(这里是一个例子):
class DataStream
{
virtual std::string read(std::string terminator) = 0;
virtual size_t write(std::string data) = 0;
};
对此有具体的实现,如:
class SerialDataStream : public DataStream
{
public:
// NOTE: This constructor will throw an exception if the
// serial port cannot be opened.
SerialDataStream(string port, int baudrate);
std::string read(std::string terminator);
size_t write(std::string data);
}
并使用接口,例如:
class SomeThing
{
public:
SomeThing(std::shared_ptr<DataStream> stream);
}
使用 GoogleMock,测试 SomeThing class 非常简单,您需要做的就是为接口创建一个模拟实现,例如:
class MockDataStream : public DataStream
{
public:
MOCK_METHOD1(read, size_t(std::vector<uint8_t>&));
MOCK_METHOD1(write, size_t(std::vector<uint8_t>&));
}
测试看起来像这样:
std::shared_ptr<MockDataStream> mock_stream(nullptr);
mock_stream = std::make_shared<MockDataStream>();
EXPECT_CALL(*mock_stream, write("START")).Times(AtLeast(1));
EXPECT_CALL(*mock_stream, read("\n")).Times(AtLeast(1));
SomeThing some_thing = SomeThing(mock_stream);
这很酷,因为它可以让我轻松地对 SomeThing class 如何使用 DataStream 接口进行单元测试。
但是,也存在一些代码,其工作是创建新的(具体的)DataStream 对象,我发现使用 GoogleMock 来测试它有点棘手。例如,这里是一些需要测试的代码片段:
std::shared_ptr<DataStream> datastream(nullptr);
// Try and open the serial port:
try
{
std::shared_ptr<SerialDataStream> serialstream =
std::make_shared<SerialDataStream>("/dev/tty99", 115200);
}
catch (...)
{
// Returns a nullptr
return datastream;
}
// Check if there is a known device on the other end:
datastream = std::static_pointer_cast<DataStream>(serialstream);
if (!device_is_connected(datastream))
{
datastream = nullptr;
}
return datastream;
我正在努力寻找一种有效的方法来使用 GoogleMock 测试此代码:
- 我想模拟(SerialDataStream 的)构造函数,以便它抛出异常并按预期执行失败路径。
- 我想测试下私有API"device_is_connected"使用新创建的datastream对象的成功路径
除了创建伪造的 SerialDataStream 实现并使用依赖注入来测试创建具体 DataStream 对象的代码之外,我别无选择吗?
如果是这种情况,我只需要制作 API "device_is_connected" public 这样我就可以简单地用接口的模拟实现来测试它(如上所述) 进行测试,例如:
datastream.write("DISCOVER");
string response = datastream.read("\n");
if (discovery_ok(response))
{
// do stuff
}
我相当确定我已经回答了我自己的问题并且别无选择,只能伪造 SerialDataStream class 并使用依赖注入,并将 APIs public 并通过 GoogleMock 简单地测试它们,但如果有更好的方法,我愿意接受建议 could/should 在这里做事。
看着你提供的代码片段,我发现自己在问:
该功能究竟应该做什么?好像是
(1) 创建一个 SerialDataStream 和
(2) 检查是否连接了设备。
您可以将函数分成两个(可单独测试的)部分。
仍然存在关于如何处理 std::shared_ptr<SerialDataStream> serialstream = std::make_shared<SerialDataStream>("/dev/tty99", 115200);
的问题 - 在某些时候你需要解决那里的(隐藏的)new。
我同意你的看法——依赖注入可能是解决方案。将任何 class 或函数最终负责创建模板 class/function 将允许写你(例如)
template<typename T>
std::shared_ptr<DataStream> createDatastream()
{
std::shared_ptr<DataStream> datastream(nullptr);
std::shared_ptr<T> datastream = std::make_shared<T>("/dev/tty99", 115200);
return datastream;
}
然后在您的应用程序中使用 SerialDataStream
实例化 class/function,同时使用 MockDataStream
来测试函数。
我已经创建了一个界面(这里是一个例子):
class DataStream
{
virtual std::string read(std::string terminator) = 0;
virtual size_t write(std::string data) = 0;
};
对此有具体的实现,如:
class SerialDataStream : public DataStream
{
public:
// NOTE: This constructor will throw an exception if the
// serial port cannot be opened.
SerialDataStream(string port, int baudrate);
std::string read(std::string terminator);
size_t write(std::string data);
}
并使用接口,例如:
class SomeThing
{
public:
SomeThing(std::shared_ptr<DataStream> stream);
}
使用 GoogleMock,测试 SomeThing class 非常简单,您需要做的就是为接口创建一个模拟实现,例如:
class MockDataStream : public DataStream
{
public:
MOCK_METHOD1(read, size_t(std::vector<uint8_t>&));
MOCK_METHOD1(write, size_t(std::vector<uint8_t>&));
}
测试看起来像这样:
std::shared_ptr<MockDataStream> mock_stream(nullptr);
mock_stream = std::make_shared<MockDataStream>();
EXPECT_CALL(*mock_stream, write("START")).Times(AtLeast(1));
EXPECT_CALL(*mock_stream, read("\n")).Times(AtLeast(1));
SomeThing some_thing = SomeThing(mock_stream);
这很酷,因为它可以让我轻松地对 SomeThing class 如何使用 DataStream 接口进行单元测试。
但是,也存在一些代码,其工作是创建新的(具体的)DataStream 对象,我发现使用 GoogleMock 来测试它有点棘手。例如,这里是一些需要测试的代码片段:
std::shared_ptr<DataStream> datastream(nullptr);
// Try and open the serial port:
try
{
std::shared_ptr<SerialDataStream> serialstream =
std::make_shared<SerialDataStream>("/dev/tty99", 115200);
}
catch (...)
{
// Returns a nullptr
return datastream;
}
// Check if there is a known device on the other end:
datastream = std::static_pointer_cast<DataStream>(serialstream);
if (!device_is_connected(datastream))
{
datastream = nullptr;
}
return datastream;
我正在努力寻找一种有效的方法来使用 GoogleMock 测试此代码:
- 我想模拟(SerialDataStream 的)构造函数,以便它抛出异常并按预期执行失败路径。
- 我想测试下私有API"device_is_connected"使用新创建的datastream对象的成功路径
除了创建伪造的 SerialDataStream 实现并使用依赖注入来测试创建具体 DataStream 对象的代码之外,我别无选择吗?
如果是这种情况,我只需要制作 API "device_is_connected" public 这样我就可以简单地用接口的模拟实现来测试它(如上所述) 进行测试,例如:
datastream.write("DISCOVER");
string response = datastream.read("\n");
if (discovery_ok(response))
{
// do stuff
}
我相当确定我已经回答了我自己的问题并且别无选择,只能伪造 SerialDataStream class 并使用依赖注入,并将 APIs public 并通过 GoogleMock 简单地测试它们,但如果有更好的方法,我愿意接受建议 could/should 在这里做事。
看着你提供的代码片段,我发现自己在问:
该功能究竟应该做什么?好像是
(1) 创建一个 SerialDataStream 和
(2) 检查是否连接了设备。
您可以将函数分成两个(可单独测试的)部分。
仍然存在关于如何处理 std::shared_ptr<SerialDataStream> serialstream = std::make_shared<SerialDataStream>("/dev/tty99", 115200);
的问题 - 在某些时候你需要解决那里的(隐藏的)new。
我同意你的看法——依赖注入可能是解决方案。将任何 class 或函数最终负责创建模板 class/function 将允许写你(例如)
template<typename T>
std::shared_ptr<DataStream> createDatastream()
{
std::shared_ptr<DataStream> datastream(nullptr);
std::shared_ptr<T> datastream = std::make_shared<T>("/dev/tty99", 115200);
return datastream;
}
然后在您的应用程序中使用 SerialDataStream
实例化 class/function,同时使用 MockDataStream
来测试函数。