我目前使用的产品是用 C++ 编写的 Windows 服务,未来所有新功能都将为其编写单元测试。但这产生了一个有趣的问题(至少对我而言)我们对各种事物进行了大量 Win32 调用并相应地进行操作,因此为了完成单元测试,最好测试各种输出,而不仅仅是当前系统状态。
我的问题是模拟 Win32 调用结果的最佳方法是什么?我想过两种不同的方法:
1) 将所有使用到的Win32调用都放入函数指针中,并传递给使用它们的函数或类(取决于它们被命中的次数),并使用它来获得模拟结果。
2)到处都有很多
#ifdef UNITTEST
,如果它正在调用我自己的特殊方法,或者如果没有调用普通方法。
我在这里完全偏离了基地,还是缺少基本知识?
关于 (2),大多数采用字符串参数的 Win32 函数已经将它们的通用形式定义为宏,例如来自 WinUser.h:
WINUSERAPI
int
WINAPI
MessageBoxA(
__in_opt HWND hWnd,
__in_opt LPCSTR lpText,
__in_opt LPCSTR lpCaption,
__in UINT uType);
WINUSERAPI
int
WINAPI
MessageBoxW(
__in_opt HWND hWnd,
__in_opt LPCWSTR lpText,
__in_opt LPCWSTR lpCaption,
__in UINT uType);
#ifdef UNICODE
#define MessageBox MessageBoxW
#else
#define MessageBox MessageBoxA
#endif // !UNICODE
你当然可以在你的项目中添加一个头文件来重新定义你想要模拟的 API 函数:
#ifdef UNITTEST
#undef MessageBox
#define MessageBox UnitTestMessageBox
#endif
通过重新定义名称,您可以避免在整个源代码中散布大量条件编译。
最后,我实际上采用了更多 C#-ish 方法并创建了接口,使我能够存根我想使用的 Win32 调用。
例如,我有一个名为
IRegistryOperations
的程序,其中包含 RegOpenKey
、RegQueryValueEx
、RegNotifyChange
和我正在使用的其他几个程序。在构造函数中创建了一个简单地调用真实函数的默认函数,但我也有一个采用接口的构造函数,因此我可以模拟不可靠的值等。
(不确定回答我自己的问题是否不礼貌)
我建议将 API 调用封装到结构良好的接口中。然后,您可以使用模拟对象或测试替身来测试您的业务逻辑。您不需要测试 Windows API 本身,因为数百万工作的 Windows 应用程序已经完成了这项工作。
如果您不开发硬件,则单元测试不应涉及硬件访问。这只是关于测试您的逻辑代码。
使用 Deviare API 挂钩并拦截所有 api 调用并进行单元测试。
您可以尝试用于 win32 API 模拟的谷歌测试 GMock lib 扩展:https://github.com/smalti/gmock-win32
如果可能,最好在不修改 Win32 调用的情况下使事件发生。
例如,不要让自己的
CreateFile
因文件正在使用而失败,而是用另一个程序(您从单元测试调用)专门打开文件,然后运行单元测试。
如果你必须模拟一些 win32 调用,那么最好围绕你想要进行的 Win32 调用集创建一个包装器库。那么你就不会损害主要逻辑的代码素养。