Baz
Baz

Reputation: 13125

Mocking a free function using Google Mocks

I have the following free function sig:

ReturnT getFirstAttributeHandle(ParentHandleT a, AttributeHandleT* b);

I need to pass the address of such a function to the constructor of an iterator which iterates over these handles. Since the concrete implementation of this functions accesses an external dependency, I need to mock it.

I'd like to use google mock to mock this function but I'm not sure how.

This is what I tried:

class IAttributeIterator
{
public:
    virtual ReturnT getFirstAttributeHandle(ParentHandleT a, AttributeHandleT* b) = 0;
};

class MockAttributeIterator : public IAttributeIterator
{
public:
    MOCK_METHOD2(getFirstAttributeHandle, ReturnT(ParentHandleT a, AttributeHandleT* b));
};

And then something like this:

MockAttributeIterator i;
AttributeIterator iter = AttributeIterator(i.getFirstAttributeHandle);
iter++;

But this doesn't compile, giving the error:

'MockAttributeIterator::getAttribute': function call missing argument list; use '&MockAttributeIterator::getAttribute' to create a pointer to member

Any suggestion on how I might do this?

Upvotes: 2

Views: 5261

Answers (3)

Fei
Fei

Reputation: 1498

The problem has nothing to do with google mock at all, the compiler was complaining about below statement:

AttributeIterator iter = AttributeIterator(i.getFirstAttributeHandle);

Pay attention to the expression as i.getFirstAttributeHandle, which is a member function call, so the compiler tried to match the function signature and get nothing.

If you want to pass partially applied functions safely to another class, try with boost.function/bind instead, playing with raw function pointers is quite dangerous and error prone.

Upvotes: 1

oneofmany
oneofmany

Reputation: 13

You can encapsulate the free function in a proxy class and then mock the call to the proxy method. The following code outlines how you could do this to mock CreateFileW and CloseHandle calls in your code.

The basic idea is also outlined in the gmock Cookbook.

/// Common Interface
class IWindows
{
public:
    virtual HANDLE CreateFileW(LPCWSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes,
        DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile) = 0;
    virtual BOOL CloseHandle(HANDLE hObject) = 0;
};


/// Implementation
class WindowsWrapper : public IWindows
{
public:
    WindowsWrapper(void);
    virtual ~WindowsWrapper(void);
    virtual HANDLE CreateFileW(LPCWSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes,
        DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile);
    virtual BOOL CloseHandle(HANDLE hObject);
};

/// Mock
class MockWindowsWrapper : public IWindows
{
public:
    MockWindowsWrapper() {}
    virtual ~MockWindowsWrapper() {}
    MOCK_METHOD7(CreateFileW, HANDLE(LPCWSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes,
        DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile))`
    {
        return ::CreateFileW(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
    }
    MOCK_METHOD1(CloseHandle, BOOL(HANDLE hObject))
    {
        return ::CloseHandle(hObject);
    }
};

Upvotes: 0

BЈовић
BЈовић

Reputation: 64203

No, you can not convert pointer to a member function to a function pointer (that is what the compiler is saying).

If the AttributeIterator's constructor accepts pointer to functions, then you need to create a fake function, which calls getFirstAttributeHandle method on MockAttributeIterator. Something like this :

namespace
{
    MockAttributeIterator mockObj;
    ReturnT FakeHandle(ParentHandleT a, AttributeHandleT* b)
    {
      mockObj.getFirstAttributeHandle( a, b );
    }
}

and pass pointer to FakeHandle to the constructor of AttributeIterator.

btw I just checked gmock faq, and this is even explained there (here is the link).

There are few things :

  • the code posted above is best to place in the anonymous namespace
  • to clear the expectations between tests, in your setup method (every unit test framework has one), do this :

    void setUp()
    {
      ::testing::Mock::VerifyAndClearExpectations( &mockObj ):
    }
    

Upvotes: 2

Related Questions