Need a suggestion in testing C style callback function with gooogletest gmock.
It's easy to test a callback defined with std::function
.
These 2 topics helped a lot:
But unfortunately my environment can't use std::
namespace (Arduino AVR platform).
So if I change from std::function
to C style callback like:
typedef void (*CallbackFunction)(void *);
then it doesn't compile, and I get the following error:
error: cannot convert ‘std::function<void(void*)>’ to ‘Foo::CallbackFunction’ {aka ‘void (*)(void*)’}
Any help appreciated.
Please see the code below:
#include <gtest/gtest.h>
#include <gmock/gmock.h>
using ::testing::_;
using ::testing::MockFunction;
using ::testing::Return;
class Foo
{
public:
using CallbackFunction = std::function<void(void *)>; //* works just fine
// typedef void (*CallbackFunction)(void *); //! doesn't work
void SetCallback(CallbackFunction callback)
{
_callback = callback;
}
void InvokeCallback()
{
_callback(nullptr);
}
private:
CallbackFunction _callback;
};
class FooTest : public testing::Test
{
public:
using CallbackFunctionMock = testing::MockFunction<void(void *)>;
CallbackFunctionMock callbackFunctionMock;
};
TEST_F(FooTest, simple)
{
EXPECT_CALL(callbackFunctionMock, Call(_)).Times(1);
Foo foo;
foo.SetCallback(callbackFunctionMock.AsStdFunction());
foo.InvokeCallback();
}
The compiler is right - there's no way to convert the object of std::function to a function pointer in a way you want to use it (or I don't know such trick, maybe someone here can show me otherwise).
You can mock this method by hand using global state. This is kinda ugly, but it gets the work done. I'd switch to std::function
whenever possible, but as long as you have to deal with raw ptrs, this can help you out:
#include <gtest/gtest.h>
#include <gmock/gmock.h>
using ::testing::_;
using ::testing::MockFunction;
using ::testing::Return;
class Foo
{
public:
// using CallbackFunction = std::function<void(void *)>; //* works just fine
typedef void (*CallbackFunction)(void *); //! doesn't work
void SetCallback(CallbackFunction callback)
{
_callback = callback;
}
void InvokeCallback()
{
_callback(nullptr);
}
void InvokeCallback(int* int_ptr)
{
_callback(int_ptr);
}
private:
CallbackFunction _callback;
};
// global state - should be kept in test.cpp
namespace {
int callback_call_counter = 0;
static void* callback_arg = nullptr;
}
void CallbackFunctionMock(void* arg) {
++callback_call_counter;
callback_arg = arg;
}
class FooTest : public testing::Test
{
public:
// FooTest ctor is called before each FooTest test case, so resetting the global variables
// to the correct state can be handled here
FooTest() {
callback_call_counter = 0;
callback_arg = nullptr;
}
};
TEST_F(FooTest, simple)
{
Foo foo;
foo.SetCallback(CallbackFunctionMock);
foo.InvokeCallback();
ASSERT_EQ(1U, callback_call_counter);
ASSERT_THAT(callback_arg, testing::IsNull());
}
TEST_F(FooTest, simple_int)
{
int i = 42;
Foo foo;
foo.SetCallback(CallbackFunctionMock);
foo.InvokeCallback(&i);
ASSERT_EQ(1U, callback_call_counter);
ASSERT_THAT(callback_arg, testing::Not(testing::IsNull()));
ASSERT_EQ(callback_arg, &i);
ASSERT_EQ(*static_cast<int*>(callback_arg), 42);
}