Search code examples
c++c++17googletestgooglemock

How to mock dll function using Gmock?


I have searched on this and did not find much information.

I have a below pattern of code where I have 1 class dedicated to load and unload dll in constructor and destructor respectively. Also the class has member variables which will hold the address to the functions to the dll.

Now this DllLoader is used by some other class. I want to mock the DllLoader so that the test I write for other class is specific to that function.

Example:

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;
};

I have tried the below, but I get exception while calling the fun1() inside init(). What am I doing wrong here?

I have not tested the code given as example here. But the flow is same.

//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
});
}

Solution

    1. Define some interface:
    class SomeFunctionality
    {
    public:
        virtual ~SomeFunctionality();
        virtual int function1(int) = 0;
    };
    
    //cpp
    SomeFunctionality::~SomeFunctionality() = default;
    
    1. Provide implementation of this interface for production case, when dll is used:
    class SomeFunctionalityFromDll : public SomeFunctionality
    {
    public:
        explicit SomeFunctionalityFromDll(const std::string& dll_name)
        {
            handle = ::LoadLibrary(dll_name);
            fun1 = (fun_ptr1)GetProcAddress(handle, "DLLFunction1");
        }
    
        ~SomeFunctionalityFromDll()
        {
            FreeLibrary(handle);
        }
    
        SomeFunctionalityFromDll(const SomeFunctionalityFromDll&) = delete;
        SomeFunctionalityFromDll& operator=(const SomeFunctionalityFromDll&) = delete;
    
        int function1(int x) override
        {
            return fun1(x);
        }
    
    private:
        fun_ptr1 fun1;
        HANDLE handle;
    };
    
    1. Use interface in production code:
    class SomeClass {
    public:
        explicit SomeClass(SomeFunctionality* dependency) : dependency(dependency) 
        {}
    
        void init() {
            int res = dependency->function1(100);
            if(res < 50) {
                //
            } else {
                //
            }
        }
        SomeFunctionality* dependency;
    };
    
    1. Then providing a mock is trivial:
    class MockSomeFunctionality : public SomeFunctionality
    {
    public:
        MOCK_METHOD(int, function1, (int), (override));
    };
    
    1. And writing test code is simple too:
    TEST(SomeClassTest, initWillInvokeFunction1)
    {
        MockSomeFunctionality mock;
        EXPECT_CALL(mock, function1(100)).WillOnce(::testing::Return(12));
        SomeClass prodClass{&mock};
        prodClass.init();
    }
    

    https://godbolt.org/z/KbevWWcnn

    The disadvantage is that you have extra indirection to call "DLLFunction1". If this is a problem it can be removed by static polymorphism (using templates).