Testing mocked functions with link-time substitution?

1,006 views
Skip to first unread message

David Rea

unread,
Mar 3, 2014, 11:41:16 PM3/3/14
to cppu...@googlegroups.com
I've done some basic embedded TDD using CppUTest and the methods prescribed in JG's book, but I'm having trouble finding background on a link-time substitution issue:

When production code contains dependencies (such as on a custom driver) and we want to test both the dependent-code and the depended-on-code, is it possible to do link-time substitution with a single build/executable? To borrow from the book - what if we wanted to test both the flash driver of chapter 10, and the higher-level code that makes use of it? I suspect that link-time substitution wouldn't work, as the linker will link against only one instance of each function for the final build.

If this is indeed the case, what is the preferred approach for testing both dependent-code and depended-on-code? Script a smaller test build for each layer of the design?

Many Thanks,
Dave

Terry

unread,
Mar 4, 2014, 12:06:10 AM3/4/14
to cppu...@googlegroups.com
Hi,

In this case, I would choose from either separate executables or function pointers.

I would prefer function printers. But that depends on who I’m working with, because I know a lot of people don’t like function pointers.

br, Terry

--
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/groups/opt_out.

Ryan Hartlage

unread,
Mar 4, 2014, 7:16:05 AM3/4/14
to cppu...@googlegroups.com
Hey Dave,

I'm involved with several large-ish programs using CppUTest and TDD.  We're using function pointers because of the issues you mentioned.  It may take a bit to get used to working in a new way, but it is far more flexible and has other benefits (it helps us to work in an OO style).  Using link-time substitution is a great way to get some practice, but it's not a good foundation to build on.

Ryan

Martin Ertsås

unread,
Mar 4, 2014, 7:33:55 AM3/4/14
to cppu...@googlegroups.com
I completely agree that function pointers are the way to go. Not only because it makes the tests more flexible, but it makes the code cleaner and creates looser couplings between the code (which at last returns in flexible and clean tests).

- Martin

David Rea

unread,
Mar 4, 2014, 7:56:04 AM3/4/14
to cppu...@googlegroups.com
Terry, Ryan, Martin-

Thank you all for the good feedback! Sounds like FPs gets you the best of both worlds - a single test build with the ability to test both dependent and depended-on code in the same fixture. I'm very comfortable with FPs and I'll explore this approach further...

I think the "multiple smaller test builds" approach would be good for a situation where you're trying to get multiple layers of legacy code under test, but don't want to refactor the interfaces between the layers (yet...).

Cheers & Thanks again,
Dave

James Grenning

unread,
Mar 4, 2014, 9:45:53 AM3/4/14
to cppu...@googlegroups.com
Hi Dave

Its good to hear everyone sighing in for FPs. FP are a good way to go, though linker substitution still has its place, like when you don’t need both in the test build.  For example stubbing the OS, HW, and other 3rd party dependencies.

BTW: I have run into another way to have your code and stub it too under gcc.  It’s called linker wrapping.  

When you set this LDFLAG to  -Wl,—wrap=Foo

From a linker perspective (not file scope perspective) Foo goes away and alls to Foo are resolved by a new symbol  __wrap_Foo.  The real Foo gets the global name __real_Foo.

So you write your fake/mock and put in into __wrap_Foo and test your production version by calling __real_Foo.

I’ve found this has limitations.  Last time I tried it it did not work in Mac OSX, cygwin, or MinGW.  Linux gcc does it just fine.  Also within a compilation unit, there linkage is not effective, a call to Foo goes to Foo.

James

--------------------------------------------------------------------------------------------
James Grenning Author of TDD for Embedded C

A. Robert S.

unread,
Mar 12, 2014, 1:02:16 PM3/12/14
to cppu...@googlegroups.com
Hi Dave,
 
We use function pointers as well. We have gone one step further in that for much-faked modules, we write generic stubs that allow for swapping in mocks, handwritten stubs, dummies (default) or even the original code at runtime.
 
Swapping in the original code requires an #include "original.c" (which is slightly ugly ;-). I write the wrapper code in C++, which results in the original function names being mangled, so I can call them from wrapper functions declared as extern "C". I used to declare them static before, but that's tricky and not really clean. The C++ approach is a lot nicer.
 
Once such stub is written, it never needs to be touched again, well, hardly ever (interface changes). Different stubs can be linked for different tests, if necessary, Dummies and mocks are always the same anyway.
 
I have taken to writing dummies in C++ as well, because it allows you to omit parameter names when you don't plan on using them. This eliminates the need for UNUSED() macros when compiling with -Werror and -Wall. The C application doesn't care, as long as the dummies are declared extern "C".
 
Cheers,
Robert

A. Robert S.

unread,
Mar 12, 2014, 1:25:15 PM3/12/14
to cppu...@googlegroups.com
Having said that, for the scenario you describe, mostly we compile tests for dependent-code and depended-on-code to different exectuables.
 
If I just wanted to be able to link all tests into one executable, then I guess I would prefer a less "bulky" approach. It would still involve wrapper functions, but maybe I would let them call the operations of a pure virtual class, so in a given test, I could instanciate them to a suitable implementation (mock, stub, dummy, or real).
 
I did find though that we often need the flexibility to mix and match dummies, mocks and handwritten stubs within the same test.
 
Robert

On Wednesday, March 12, 2014 6:02:16 PM UTC+1, A. Robert S. wrote:
Hi Dave,
Reply all
Reply to author
Forward
0 new messages