我对此进行了搜索,但没有找到太多信息。可能我需要更多搜索,但我仍然会继续在这里提出问题。
我有一个下面的代码模式,其中有 1 个类专门用于分别在构造函数和析构函数中加载和卸载 dll。该类还具有成员变量,用于保存 dll 函数的地址。
现在这个
DllLoader
被其他一些类使用。我想模拟 DllLoader
,以便我为其他类编写的测试特定于该函数。
示例:
typedef int(*fun_ptr)(int);
class DllLoader {
public:
DllLoader(const std::string& dll_name) : handle(nullptr), fun1(nullptr) {
handle = ::LoadLibrary(dll_name);
fun1 = (fun_ptr1)GetProcAddress(handle, "DLLFunction1");
}
fun_ptr1 fun1;
HANDLE handle;
};
class SomeClass {
public:
SomeClass(DllLoader* dll_loader) : dll(dll_loader) {}
void init() {
int res = dll->fun1(100); // I want to use mock for fun1() call. Exception!!!
if(res < 50) {
//
}
else {
//
}
}
DllLoader* dll;
};
我尝试了以下方法,但在 init() 内调用 fun1() 时出现异常。不知道我在这里做错了什么。有人可以帮我吗?
我尚未测试此处作为示例给出的代码。但流程是一样的。
//created a mock class for DllLoader
class MockDllLoader : public DllLoader {
public:
MockDllLoader() : DllLoader("");
MOCK_METHOD(int, fun1, (int));
};
TEST(SomeClassTest, TestInit) {
MockDllLoader *mock_dll_loader = new MocDllLoader();
SomeClass * some_class = new SomeClass(mock_dll_loader);
EXPECT_CALL(*mock_dll_loader, fun1(::testing::_)).WillOnce(Return(1000));
EXPECT_NO_THROW({
some_class->init(); //expecting init to use the mocked function
});
}
使代码易于测试很难。
但是,在您的示例代码中(如果
fun1
是公共的)它可以像设置成员变量一样简单。
这是一个低成本的解决方案,通过将值设置为虚拟函数。当然,如果你需要mock能力,你可以使用
return_self
函数来创建一个mock对象并设置一些期望。
typedef int(*fun_ptr)(int);
class DllLoader {
public:
DllLoader() {
handle = ::LoadLibrary(dll_name);
fun1 = (fun_ptr1)GetProcAddress(handle, "DLLFunction1");
}
fun_ptr fun1;
HANDLE handle;
};
int return_self(int i) {
return i;
};
int main(void)
{
DllLoader d;
d.fun1 = &return_self;
std::cout << d.fun1(10);
return 0;
}
如果它不是公开的(但您可以更改
DllLoader
类),我会在 DllLoader
之上创建一个接口,并在单元测试中使用模拟接口,并在实际应用。类似这样的:
DllLoader
然后就像将模拟的
class IDllLoader {
public:
void callFunction() = 0;
};
class DllLoader : public IDllLoader {
...
};
传递给您的
IDllLoader
一样简单,然后您就可以设置测试了。通过使用该接口,您还可以避免调用真正的 SomeClass
的构造函数,因此跳过
DllLoader
和 ::LoadLibrary
调用(我认为在单元测试期间会很好)。