Alex Alex - 17 days ago 5
C Question

Using google mock for C code

I'm maintaining a legacy project written in C and it's unfeasible to get it running with a C++ compiler. Since the code is cross compiled it is however possible to run unit-tests or similar in a host environment. hence it's also possible to interface with a C++ host compiler and use google-test and google-mock.

There are certain capabilities of google-mock which seem to be very tempting to be used for testing as invoking real implementations and setting call expectations.

I would like to be able to use them in C code. I can see that it is indeed possible to use google-mock without using vtables, but it requires templates.

Is there a way to mock bare C functions with google mock?

Answer

I found a way to be able to mock bare C functions in google-mock.

The solution is to use weak aliases on the host and strong aliases on the target.

The following solution is GCC specific but there should be other solutions for different compiler/linkers.

  • rename the function void foobar(); to void foobarImpl();
  • add an attribute to the function foobar like: void foobar() __attribute__((weak, alias("foobarImpl") ));
  • if you want to have a non weak alias use a preproessor directive to remove the weak from the attributes.

Hence:

#pragma once
void foobar();

becomes

// header.h
#pragma once

void foobar();    
void foobarImpl(); // real implementation

and

// code.c
void foobarImpl() {
  /* do sth */
}
void foobar() __attribute__(( weak, alias ("foobarImpl") )); // add weak reference

This will tell the gnu linker to link calls of foobar() with foobarImpl() whenever there is no symbol called foobar()

then add the testing code

struct FooInterface {
   virtual ~FooInterface() {}
   virtual void invokeFoo() const { }
};

class MockFoo : public FooInterface {
public:
  MOCK_CONST_METHOD0(invokeFoo, void());
}

struct RealFoo : public FooInterface {
   virtual ~RealFoo() {}
   virtual void invokeFoo() const { FooImpl(); }
};

MockFoo mockFoo;
RealFoo realFoo;
void foobar() {
  mockFoo.invokeFoo();
}

if this code is compiled and linked it will replace foobar with the mock call. if you really want to call foobar() you can still do add a default invocation.

ON_CALL(mockFoo, invokeFoo())
       .WillByDefault(Invoke(&realFoo,&RealFoo::invokeFoo));