Mocked vs. Unmocked version of a function.

958 views
Skip to first unread message

Markus Grunwald

unread,
Dec 18, 2015, 9:14:03 AM12/18/15
to cpputest
Hello,

say I have one testfile, ATests.cpp where I test functions of module A. There is a test like this:

TEST( ATests, TestSomeFunction )
{
  mock().expectOneCall("Tau")
    .withParameter("p1",17 )
    .andReturnValue( 42 );

  SomeFunction( );
}

So I expect that SomeFunction will call Tau(17) and return 42. To test this, I have to write a mock for Tau.

Now tests go on and I write a test file BTests.cpp where I want to test the module B, where Tau is implemented. there, I want to test Tau itself:

TEST( ATests, TestTau )
{
  CHECK_EQUAL( 1, Tau(5) );
  CHECK_EQUAL( 2, Tau(7) );
  CHECK_EQUAL( 42, Tau(17) );
}

When I run the compiler, I get a linker error:
B.c:49: multiple definition of `Tau'
BMock.o:BMock.cpp:8: first defined here


Of course, I have two definitions: one mock and the original. I just started writing tests, but I expect this to be quite a common situation: in one set of tests, I want to test higher level functions where I need mocks, and in another set of tests, I want to test the lower level functions where I need the functions themselves...

James Grenning has proposed a solution to this problem: use function pointers to these functions https://wingman-sw.com/articles/tdd-legacy-c

Create a function pointer with the same signature as the problem function. By default initialize the pointer with the problem function. Change clients to call through the function pointer. Override the function in the tests where needed.

At the rate I think this will happen, this is not a solution that I would like to use. A colleage of mine has resortet to use completely separate test projects for each test!

This scares me, somehow. Is there no better way? ( I can't use gcc linker wrapping, because I'm not developing with gcc/linux)...

Do you have any advice?

Thanks,
Markus Grunwald



Ryan Hartlage

unread,
Dec 18, 2015, 10:05:14 AM12/18/15
to cpputest
Unfortunately your only real options are to use separate test projects (which is a huge pain) or to refactor your existing code to use function pointers to implement something like interfaces in C. It can be a bit painful to deal with interfaces in C, but in the long run it's a lot less painful than maintaining separate test projects and I will argue that it will improve your code overall to code to interfaces instead of implementations. My teams have found success with this strategy, but unfortunately it's much easier if you're starting from scratch.

Ryan

Bas Vodde

unread,
Dec 20, 2015, 2:22:28 AM12/20/15
to cppu...@googlegroups.com

Hi Markus,

If the Tau is the only function in the object file that you use, then you might be able to link the object files in a library and link the library.

Otherwise, you’ll need to avoid linking original, or seperating it out in a seperate object file, or use Function-Pointer stubbing (which would probably be preferred)

Thanks,

Bas


--
You received this message because you are subscribed to the Google Groups "cpputest" group.
To unsubscribe from this group and stop receiving emails from it, send an email to cpputest+u...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Markus Grunwald

unread,
Jan 14, 2016, 2:14:39 AM1/14/16
to cpputest
Hello Ryan,

thanks for your Answer! I went the way of interfaces for now and it doesn't seem to be too bad ;)

Many thanks for your Help!

Markus Grunwald

unread,
Jan 14, 2016, 2:16:00 AM1/14/16
to cpputest
Hello Bas,


If the Tau is the only function in the object file that you use, then you might be able to link the object files in a library and link the library.

As there were more functions and this seems to be a general problem, I'm trying to use interfaces now.
Nevertheless, thanks for your answer! 

A. Robert S.

unread,
Jan 21, 2016, 8:40:27 AM1/21/16
to cpputest
Hi Markus,
 
Actually, it is not necessary to refactor your production code in order to work with function pointers. It is possible working with C++ on the test side to create function pointer interfaces that the C code doesn't have to know about. Basically, each function is defined as a wrapper (replacing the original function and getting linked), which calls the function pointer, that can be manipulated in your tests. The original function gets #included in a C++ namespace (working with namespaces is neat, but the name mangling in itself would be sufficient to fool the linker).
 
Regards,
Robert

Pierre-Yves Schütz

unread,
Mar 14, 2016, 10:32:18 AM3/14/16
to cpputest
Hi Robert,

Could you provide a short example showing how to implement that? It looks like an interesting solution.

Regards
Pierre

Pierre-Yves Schütz

unread,
Mar 14, 2016, 11:52:27 AM3/14/16
to cpputest
OK, I implemented it in the following way, thank you for the hints!

timer.h:
#ifdef UNIT_TESTS
#include <test_timer.h>
namespace prod {
#endif

void timer_init(void);

#ifdef UNIT_TESTS
}
#endif

timer.c:
#include <timer.h>

#ifdef UNIT_TESTS
namespace prod {
#endif

void timer_init(void) {}

#ifdef UNIT_TESTS
}
#endif


timer_stub.cpp:
#include <test_timer.h>

namespace stub {

void timer_init(void) {
    mock
().actualCall("timer_init");
}

}

test_timer.h:
#include <timer.h>

namespace stub {

void timer_init(void);

}

extern void (*timer_init)(void);


test_timer.cpp:
#include <CppUTest/TestHarness.h>
#include <CppUTestExt/MockSupport.h>
#include <test_timer.h>
#include <timer.h>

void (*timer_init)(void) = stub::timer_init;

TEST_GROUP
(timer) {};

TEST
(timer, init) {
    prod
::timer_init();
}

A. Robert S.

unread,
Mar 17, 2016, 3:11:11 AM3/17/16
to cpputest
Hi again,

and sorry for the late reply. There are some worked examples at FakeFunctions. Regarding Pierre's example - one fundamental thing I'd recommend to do differently is this:

timer.h -- no add-ons for unit test here

timer.c -- no add-ons for unit test here, either

timer_stub.cpp:
#include <test_timer.h>

namespace stub {

void timer_init(void) {
    mock
().actualCall("timer_init");
}

}

namespace prod {

#include "timer.c"
 // sic! This makes it prod::timer_init() only for testing

}

extern "C" {

void timer_init(void) {
    // This is the stub that gets linked instead of the original.
    // It calls a function pointer that is set to prod::timer_init()
    // and can be set to stub::timer_init() when needed.
}

}


I left out the details of the function pointer mechanism, you can find that in the example I mentioned above.

Pierre-Yves Schütz

unread,
Mar 24, 2016, 3:53:09 AM3/24/16
to cpputest
Clever solution. Thanks!

Markus Grunwald

unread,
Sep 27, 2016, 8:40:28 AM9/27/16
to cpputest
Hello Robert


and sorry for the late reply. There are some worked examples at FakeFunctions. Regarding Pierre's example - one fundamental thing I'd recommend to do differently is this:


As the initiator of this thread, I'd like to thank you for this hint! As I mentioned, I went the way of interfaces, but they are not very much welcomed in my team... Might be that this approach suits them better. I personally think, the test code grows quite a bit and test coverage analysis might be harder (because of the included test subject).
I'll definitely give it a try.

Thanks again! 

William Huang

unread,
Apr 16, 2018, 2:35:00 PM4/16/18
to cpputest
Hello, sorry to bring an old post back up but I thought my issue was relevant so I reposted here.

I am attempt to  implement the suggestion made by Robert, however I am getting an 'error: call of overloaded FUNCTION_NAME is ambigous'.

Here is some code that replicates my error.

FakeAdd.c
#include "FakeAdd.h"


int (*FakeAdd::add)(int,int) = Prod::add;
int (*FakeAdd::actualAdd)(AddStruct_t *) = Prod::actualAdd;

extern "C" int add(int a,int b)
{
   
return FakeAdd::add(a,b);
}

extern "C" int actualAdd(AddStruct_t *pAddStruct)
{
   
return FakeAdd::actualAdd(pAddStruct);
}

namespace Stub
{
   
int add(int a,int b)
   
{
       
AddStruct_t stubStruct;
        stubStruct
.a = a;
        stubStruct
.b = b;

       
return actualAdd(&stubStruct); // ambigous
    }
   
int actualAdd(AddStruct_t *pAddStruct)
   
{
       
return pAddStruct->a + pAddStruct->b;
   
}
}

namespace Prod
{
#include "Add.c"
}


FakeAdd.h:
#ifndef FAKEADD_H
#define FAKEADD_H

extern "C"
{
#include "Add.h"
};

class FakeAdd
{
public:
static int (*add)(int,int);
static int (*actualAdd)(AddStruct_t *);
};

namespace Stub
{
int add(int a,int b);
int actualAdd(AddStruct_t *pAddStruct);
}

namespace Prod
{
int add(int a,int b);
int actualAdd(AddStruct_t *pAddStruct);
}

#endif //FAKEADD_H

Add.c:
#include "Add.h"

int add(int a,int b) 
    AddStruct_t addStruct;
    addStruct.a = a;
    addStruct.b = b;
    
    return actualAdd(&addStruct); 
}
int actualAdd(AddStruct_t *pAddStruct)
{
    return *pAddStruct.a + *pAddStruct.b;
}


Add.h:
#ifndef _ADD_H
#define _ADD_H

typedef struct AddStruct_s
{
    int a;
    int b;
} AddStruct_t;

extern int add(int a,int b);
extern int actualAdd(AddStruct_t *pAddStruct);

#endif

I am quite stuck at this error and any advice would be much appreciated!
Reply all
Reply to author
Forward
0 new messages