Using google test to check callbacks
Asked Answered
U

1

9

I have a class Foo that stores a pointer to a callback. The callback could be invoked with a method InvokeCallback().

void* SomeCallback(void* a) {

  return (void*)(*(int*)a + 10);

}


class Foo {

public: 

    typedef void* (*CallbackFunction)(void*);

    SetCallback(CallbackFunction callback) {
        this->callback = callback;
    }

    InvokeCallback() {
        callback(20);
    }

private:

    CallbackFunction callback;

}

How is it possible to test (using google test) that SomeCallback() was invoked with a specific parameter (20 in the abovementioned case)?

EDIT:

I came up with a very ugly looking solution. The tests are passed, but the new error appeared at the end ERROR: 1 leaked mock object found at program exit.

class MockCallbackClass {
public:
    MOCK_METHOD1(SomeCallback, void*(void*));
};

MockCallbackClass mock; // Nasty global variable

void* __SomeOtherCallback(void* a) {

    return mock.SomeCallback(a);

}

TEST(Callback, MockCallback) {

    Foo foo;
    foo.SetCallback(__SomeOtherCallback);
    EXPECT_CALL(mock, SomeCallback((void*)10)).WillOnce(testing::Return((void*)20));

}
Uniaxial answered 16/2, 2017 at 6:37 Comment(7)
Possible duplicate: #8942830Linctus
@πάντα ῥεῖ, I checked the question before and didn't figure out how to apply the answer to the case of standalone function callbacks.Uniaxial
You cannot. Google Mock interfaces require a class interface.Linctus
@πάντα ῥεῖ I'm open to use classes. I came up with some solution (in the edit), but it looks quite ugly.Uniaxial
Why not passing an abstract interface to Foo in 1st place? Are you able to change Foo?Linctus
@πάντα ῥεῖ Unfortunately, I can't change Foo.Uniaxial
@MikeMB Thank you very much for the correction!Uniaxial
G
12

Use std::function<void*(void*)> and testing::MockFunction<void*(void*)> to mock this std::function. Of course this requires slight changes in your implementation - but functionality of your design does not change, because raw function pointers can be stored in std::function without problems:

class Foo {
public: 

    using CallbackFunction = std::function<void*(void*)>;
    // rest of this class is as it was
};

And test like this:

class FooTest : public testing::Test {
public: 

    using CallbackFunctionMock = testing::MockFunction<void*(void*)>;
    CallbackFunctionMock  callbackFunctionMock;

    Foo  objectUnderTest{[this](void* v) { return callbackFunctionMock.Call(v); }};
};
TEST_F(FooTest, shallCallbackBeCalledByInvoke)
{
    int a = 40;
    EXPECT_CALL(callbackFunctionMock, Call(reinterpret_cast<void*>(20)).WillOnce(Return((void*)(&a));
    objectUnderTest.InvokeCallback();
}
Gaziantep answered 17/2, 2017 at 13:26 Comment(4)
Can you explain the line in FooTest where the lambda is used? How does this equate to the OP's need to set the callback function? I have a similar situation where I pass a std::function to the class to store the callback. @GaziantepFlyman
@Mr.Shickadance lambda expression works there as std::bind - it is there to "casts" MockFunction to std::functionGaziantep
On MockFunction you can also call AsStdFunction() to cast it to std::function and then pass it as an argument.Usurer
@Usurer you're right, that is even simpler and easier to understand ... this answer here provides an example on how to do that https://mcmap.net/q/473856/-how-to-use-gmock-to-mock-up-a-std-functionDenicedenie

© 2022 - 2024 — McMap. All rights reserved.