单元测试硬件的应用程序接口 - 模拟与否

时间:2016-08-03 13:49:30

标签: c++ unit-testing c++11 googletest

我非常好奇人们对与硬件接口的软件应用程序应该以何种方式进行单元测试的意见。

例如,软件应用程序“Connection”的主类将构建USB设备的句柄。

我想测试“Connection”类基本功能,说“OpenConnection”会尝试连接USB硬件设备。

到目前为止我已经构建了一个MOCK硬件设备,并且在我的连接类中包含了一个编译器标志,所以如果它内置单元测试模式,它将使用一个模拟对象,另外它会使用实际的硬件接口。 / p>

见下面的例子

class TConnection
{
public:
    static TConnection* GetConnection();
    static void Shutdown();

    bool DidInitialise();

    bool Write(uint8_t* _pu8_buffer);
    bool Read(uint8_t* _pu8_buffer);

protected:
    TConnection();
    virtual ~TConnection();
    bool init();

private:
    static TConnection* mp_padConnection;
    static bool mb_DidInitialise;

#ifdef _UNIT_TEST_BUILD
    static mock_device* mp_handle;
#else
    static device* mp_handle;
#endif
};

然后在源文件中包含类似

的内容
#include "connection.h"

#ifdef _UNIT_TEST_BUILD
    mock_device* TConnection::mp_handle = nullptr;
#else
    device* TConnection::mp_handle = nullptr;
#endif // _UNIT_TEST_BUILD

TConnection::TConnection()
{
    ...
    init();
    ...
}

bool TConnection::init()
{
    mp_handle = hid_open( _VENDOR_ID, _PRODUCT_ID, nullptr );
    if (mp_hidHandle == nullptr) {
        return false;
    }
    if (hid_set_nonblocking(mp_hidHandle, _DISABLE_NB) == _ERROR_CODE) {
        return false;
    }
    return true;
}

我唯一不喜欢我的代码是我的实际连接类包含测试代码。我更希望他们分开。

这样说,我也不同意有一个全新的模拟连接类,仅仅是为了单元测试而编写的,它让我觉得我只是想写一些能够按预期工作的东西。

所以我想问一下,测试这样一个类的更好的方法是什么

提前感谢您的时间和建议

2 个答案:

答案 0 :(得分:5)

您可以通过使用依赖项注入来避免向您的类添加测试代码。创建接口IDevice并使类Device实现该接口。然后,在类TConnection中,使用指向此接口的指针而不是类型Device的成员。还可以创建一个帮助方法,允许您设置新设备,如:

void setDevice(IDevice *device);

现在,对于您的生产代码,简单地使用类Device的实例,而在您的测试代码中使用setDevice来交换设备的实现与模拟对象。这个模拟对象将是类MockDevice的一个实例,它也将实现接口IDevice。这样你就可以改变测试中的实现并使用mock类。由于您已经使用 gtest ,我建议您不要自己编写模拟类,而是使用C ++模拟框架 gmock (与gtest完全兼容)。这样,您还需要创建一个单独的类,但几乎所有内容都将由模拟框架处理。您需要做的就是定义模拟方法。创建一个额外的界面和模拟类似乎一开始就有点过头了,但从长远来看它肯定会有所回报。如果你想对代码进行任何严格的测试驱动,学习使用接口,依赖注入和模拟类是必不可少的。查看文档以获取更多详细信息:

https://github.com/google/googlemock/blob/master/googlemock/docs/CheatSheet.md

答案 1 :(得分:3)

就个人而言,我将模拟作为单独的类或测试代码的一部分。为了区分模拟库和实际库,我会在构建脚本中进行更改,我假设这些更改包括链接到库的测试文件(和模拟)

创建单独的类不是浪费精力。它应该按预期运行,但这可以简化为测试所需的最低限度。更有趣的是让类生成错误事件,以确保您的代码正确处理这些事件。另一种方法是等待错误发生,我不建议这样做。

关于这个主题的两本强烈推荐的书籍: