Home > other >  Can you mock C functions with Googlemock without creating a global mock instance?
Can you mock C functions with Googlemock without creating a global mock instance?

Time:12-02

The generally used approach of wrapping free C functions in a "Mockinterface" class with a child class "Mock"(that calls MOCK_METHOD) results in the need of a global mock variable in order to use it in a TEST.

Is there another design approach that does not need a global Mock object/pointer so we could use a local Mock object in TEST?

CodePudding user response:

It doesn't matter whether the mock object is globally static, locally static, or locally dynamic. You just need to make sure that it exists when the mocked C function is called and wants to call the method of the mock object.

The important part is that the per-se static C functions need to access that mock object. In consequence you need some kind of static variable. It does not need to be the mock object.

I realized this with a class variable (in contrast to a member variable) that stores a pointer to the single mock object. If you are sure that at most one mock object exists at a time, you can even save some effort because of possibly multiple instantiations.

For example:

class FunctionsMock
{
public:
    static FunctionsMock* instance;

    FunctionsMock()
    {
        instance = this;
    }

    ~FunctionsMock()
    {
        instance = nullptr;
    }

    MOCK_METHOD(int, function1, (int p));

    MOCK_METHOD(void, function2, ());
};

An implementation can look like:

FunctionsMock* FunctionsMock::instance = nullptr;

extern "C" int function1(int p)
{
    if (FunctionsMock::instance == nullptr)
    {
        ADD_FAILURE() << "FunctionsMock::instance == nullptr";
        return 0; // some sensible value
    }
    return FunctionsMock::instance->function1(p);
}

extern "C" void function2()
{
    ASSERT_NE(FunctionsMock::instance, nullptr);
    FunctionsMock::instance->function2();
}

Note: Since you cannot use ASSERT_NE in a function that returns a value, you need to check the condition explicitly and in case call ADD_FAILURE.

Now you can define a local mock object and check that expected functions are called:

TEST(SuiteName, TestName)
{
    FunctionsMock mock;

    EXPECT_CALL(mock, function1(...
    EXPECT_CALL(mock, function2())...

    // ...
}

If you want multiple concurrent mock objects, the C functions needs to know which mock object has to be used. I did not dive into this since we did not need it. All our tests run sequentially. You might want to do some research, depending on the kind of multitasking you do.

If you just want to use the latest instance, you can provide a static stack of mock object pointers instead of the single pointer.

  • Related