How to mock a variable argument function

4,010 views
Skip to first unread message

A. Robert S.

unread,
Sep 13, 2011, 5:53:51 AM9/13/11
to cpputest
Hi everyone,

in our legacy code, there is a printf()-like trace function.

In unit testing, there are many places, where I would prefer to stub
this out, so the output won't be cluttered by messages, and also,
because it has a lot of dependencies that I don't care to maintain
when testing.

Now what I would like to do is being able to write a mock that can
handle a variable argument list.

Here's my problem: At compile time, I would need to have a loop in
order to add my expected arguments.

So, instead of specifying them all at once, like:

mock().expectOneCall( "Trace_Print" )
.withParameter( "bBool", bBool )
.withParameter( "trace", trace )
.withParameter( "pcFormatString", pcFormatString )
.ignoreOtherParameters()
.andReturnValue( (int) status );

I would need to add on to the mock in a loop, something like:

loop:
{
mock().addParameter( "myArg1" );
}

At the moment, I can only use vsnprintf() or the like, and test the
resulting message string against a string. I consider this method of
testing both brittle and error-prone.

Would it be difficult to implement addParameter() for the Mock object?

(Apologies, I am more of a C programmer and haven't had a chance to
use C++ much. Also, I don't really understand all of the inner
workings of mocks in CppUTest)

Robert

James Grenning

unread,
Sep 13, 2011, 1:00:22 PM9/13/11
to cppu...@googlegroups.com
Hi Robert

I suspect that each invocation has a known number of parameters. In the setup phase of the specific test case you know what you expect, so maybe you don;t need a loop. If you are trying to refactor out duplication, you could have helper functions that use 1, 2, 3, ... of the optional parameters.

If you really need to do it though, you can probably setup the test with CppUMock as is. Notice the return type of expectOneCall, and expectNCalls:

class MockSupport
{
public:
MockSupport();
virtual ~MockSupport();

virtual void strictOrder();
virtual MockFunctionCall& expectOneCall(const SimpleString& functionName);
virtual MockFunctionCall& expectNCalls(int amount, const SimpleString& functionName);
.....

It returns a reference to a MockFunctionCall. That's how the mock calls can be strung together. Though I have not done this, and am not sure if it is a good idea...

MockFunctionCall& mockCall = mock().expectOneCall( "Trace_Print" );

now you can loop like this:

loop:
{
mockCall.addParameter( "myArg1" );
}


close with this:

mockCall.ignoreOtherParameters()
mockCall.andReturnValue( (int) status );


Maybe Bas will comment

James

A. Robert S.

unread,
Sep 14, 2011, 5:50:13 AM9/14/11
to cpputest
Hi James,

Thank you for bearing with me here. I can see where your approach is
going. Looks very good. Now I need to figure out how the non-OO
equivalent mock_c() is strung together and get that to work. I wish I
had a better grasp of C++ here...

Robert
> > Robert- Hide quoted text -
>
> - Show quoted text -

James Grenning

unread,
Sep 14, 2011, 7:19:06 AM9/14/11
to cppu...@googlegroups.com
You will probably learn some by osmosis :-)

Bas Vodde

unread,
Sep 14, 2011, 1:02:02 PM9/14/11
to cppu...@googlegroups.com

Hi,

The mock_c() works the same as in C++. I've actually never seen it before in C, but it worked pretty well :)

Each call returns a struct with function pointers, so therefore you call a function pointer each time, which allows you to chain method calls in C.

Its kinda weird for a C programmer, but it worked pretty well (under the hood, it just converts to C++)

Bas

Bas Vodde

unread,
Sep 15, 2011, 2:25:29 AM9/15/11
to cppu...@googlegroups.com

Hi Robert,

James already gave an option which ought to work ;)

Personally, I always use the vsnprintf way that you described as it will make the tests look a lot nicer. From the unit test perspective, I don't really care how it formats the message, but I'd like to test that the message is what it is :)

Why do you feel that would make the tests brittle?

Bas

A. Robert S.

unread,
Sep 15, 2011, 7:31:20 AM9/15/11
to cpputest
Hi Bas,

Because, when I am testing for a number, say 345, i't's just that
number.

If I test the resulting string, I must worry about whether it will be
displayed
as "345", " 345", "0345", "345.0" etc. depending on the format
string.
Someone might change the format string (which is pretty much
irrelevant
to the tests in question) for some reason, and tests would fail that
should have passed. Maybe I'm just being pedantic, but I thought
comparing
the actual arguments would be cleaner.

Besides, in this particular case, the resulting mock will be used over
and over again, so I thought it might be worth the effort.

Arnd

A. Robert S.

unread,
Sep 15, 2011, 7:44:31 AM9/15/11
to cpputest
Hi Bas,

I did learn a lot about the inner workings of Mock_c(), but I'm not
sure a have quite wrapped my head around how exactly it strings its
functions together...

Here is what I came up with (my mock for Trace_Print()). Note that the
function being stubbed only handles a subset of what vsnprintf() does.
Also, as far as parsing the va_list is concerned, it might be somewhat
crude ;-):

uint8 ETtrace_vPrint( BOOL bNewline,
TETtraceLevel trace, const char * pcFormatString, va_list args )
{
MockFunctionCall_c * mockParameters = NULL;
uint16 wIndex = 0;
char szArgName[2] = "A";
char cLetter = 'A';

/* Set a pointer to last pointer in linked list of function
pointers */
mockParameters = mock_c()
->actualCall( "ETtrace_Print" )
->withIntParameters( "bNewline", bNewline )
->withIntParameters( "trace", trace )
->withStringParameters("pcFormatString", pcFormatString);

/* For each variable argument, find out its type and add
parameter accordingly */
while( '\0' != pcFormatString[wIndex] )
{
if ('%' == pcFormatString[wIndex])
{
wIndex++;

/* Discard with information */
while (('0' <= pcFormatString[wIndex]) &&
('9' >= pcFormatString[wIndex]))
{
wIndex++;
}

/* Name the argument "A".."z" */
szArgName[0]=cLetter;

/* Treat each argument according to its type */
switch (pcFormatString[wIndex])
{
case 'c': /* character */
mockParameters->withParameterOfType(
"char", szArgName, va_arg( args, void
*) );
break;
case 's': /* string */
mockParameters->withStringParameters(
szArgName, va_arg( args, char *) );
break;
case 'd': /* decimal */
case 'x': /* hexadecimal */
case 'X': /* hexadecimal */
mockParameters->withIntParameters(
szArgName, va_arg( args, unsigned long) );
break;
default: /* unknown */
mockParameters->withStringParameters(
szArgName, "<Unknown Type>" );
break;
}

cLetter++; /* Use next ASCII letter */
}

wIndex++;
} /* while( '\0' != *pCurrent) */

/* Finally, return mock's returnvalue */
return mock_c()->returnValue().value.intValue;
}

On Sep 15, 8:25 am, Bas Vodde <b...@odd-e.com> wrote:

Terry Yin

unread,
Sep 15, 2011, 7:59:53 AM9/15/11
to cppu...@googlegroups.com
Hi Robert,

Here's some of my thoughts. In the beginning, I agree with you, comparing the strings might be brittle. But then I changed my mind.

You will stub out the trace_print, right? So that will be part of your test, so test has control of it. Therefore there's no problem comparing the strings in the test. And of course you can also write another helper function like expect_trace_print(arguments...), and in the background you can still compare the strings.

I hope I haven't miss-red the question here:-)

-terry
--
-terry
-------------------------
Blog: http://terryyin.blogbus.com/
twitter: http://twitter.com/terryyin

A. Robert S.

unread,
Sep 15, 2011, 8:04:57 AM9/15/11
to cpputest
When I run my tests on the code below, I get memory leak errors for
successful tests (not when they fail, it seems), and more errors for
more variable arguments. Something is going wrong here, not?

../unittest/mocks/interfaces/ettrace/ETtraceStubsTest.cpp:139: error:
Failure in TEST(ETtraceStubs, PrintOneIntRecognizedcorrectly)
Memory leak(s) found.
Leak size: 64 Allocated at: src/CppUTestExt/MockSupport.cpp and line:
120. Type: "new" Content: "HÿA"
Leak size: 8 Allocated at: src/CppUTestExt/MockNamedValue.cpp and
line: 209. Type: "new" Content: "¨e "
Leak size: 32 Allocated at: src/CppUTestExt/
MockExpectedFunctionCall.cpp and line: 57. Type: "new" Content: "H B"
Leak size: 32 Allocated at: src/CppUTestExt/
MockExpectedFunctionCall.cpp and line: 57. Type: "new" Content: "H B"
Leak size: 8 Allocated at: src/CppUTestExt/MockNamedValue.cpp and
line: 209. Type: "new" Content: "Ð[ "
Leak size: 32 Allocated at: src/CppUTestExt/
MockExpectedFunctionCall.cpp and line: 73. Type: "new" Content: "H B"
Leak size: 36 Allocated at: src/CppUTestExt/MockSupport.cpp and line:
141. Type: "new" Content: "ˆýA"
Leak size: 8 Allocated at: src/CppUTestExt/MockNamedValue.cpp and
line: 209. Type: "new" Content: " \ "
Leak size: 4 Allocated at: src/CppUTestExt/
MockExpectedFunctionCall.cpp and line: 39. Type: "new" Content: "¨d "
Leak size: 8 Allocated at: src/CppUTestExt/
MockExpectedFunctionsList.cpp and line: 96. Type: "new" Content: " d "
Leak size: 32 Allocated at: src/CppUTestExt/
MockExpectedFunctionCall.cpp and line: 57. Type: "new" Content: "H B"
Leak size: 8 Allocated at: src/CppUTestExt/MockNamedValue.cpp and
line: 209. Type: "new" Content: "ðj "
Total number of leaks: 12
> > - Show quoted text -- Hide quoted text -

A. Robert S.

unread,
Sep 15, 2011, 8:11:36 AM9/15/11
to cpputest
Hi Terry,

I am with you. I also thought for a moment I am in conrtol of the
format string. But, actually, I am not. The function I am testing
controls the format string. So (assuming I do not test the argument
containing the format string as such, which wouldn't make too much
sense), if it is changed (and the other parameters stay), I must also
change the corresponding unit tests, most probably more than one per
function. If I test the variable parameters directly, nothing changes.

Arnd

On Sep 15, 1:59 pm, Terry Yin <terry.yin...@gmail.com> wrote:
> Hi Robert,
>
> Here's some of my thoughts. In the beginning, I agree with you, comparing
> the strings might be brittle. But then I changed my mind.
>
> You will stub out the trace_print, right? So that will be part of your test,
> so test has control of it. Therefore there's no problem comparing the
> strings in the test. And of course you can also write another helper
> function like expect_trace_print(arguments...), and in the background you
> can still compare the strings.
>
> I hope I haven't miss-red the question here:-)
>
> -terry
>
> twitter:http://twitter.com/terryyin- Hide quoted text -

A. Robert S.

unread,
Sep 15, 2011, 8:13:15 AM9/15/11
to cpputest
if "it ist changed" meaning the format string is changed.

A. Robert
> > twitter:http://twitter.com/terryyin-Hide quoted text -
>
> > - Show quoted text -- Hide quoted text -

Terry Yin

unread,
Sep 15, 2011, 8:45:23 AM9/15/11
to cppu...@googlegroups.com
Hi Robert,

Yes, the format string makes things complicated. I sense there's some concerns that should be separated. And it's too much a burden for every test to bear the trace printing logic.

Say if you have good UT coverage, is it necessary to have so many traces (or is it the best way to spy on the code under test)? And if the traces are for higher level logic, then it should be tested separately.

terry

A. Robert S.

unread,
Sep 15, 2011, 9:04:40 AM9/15/11
to cpputest
Hi Terry,

You are certainly right questioning the use of traces, but I have been
called to introduce unit testing in a project with terrible code
(several
suppliers, lots of #ifdef'd stuff, sometimes more logic in the #ifdefs
than in the code, so for now, I guess I have to make do with what
there is and encourage the developers to do some good refactoring
as they start writing unit tests. Also, this is a system in
production,
so you have to go gently about changing any production code.

I started out just wanting to get rid of the traces (because the code
attracts so many otherwise irrelevant headers) then on second thoughts
if figured I could add some code and use the mocked traces for
testing.....

On Sep 15, 2:45 pm, Terry Yin <terry.yin...@gmail.com> wrote:
> Hi Robert,
>
> Yes, the format string makes things complicated. I sense there's some
> concerns that should be separated. And it's too much a burden for every test
> to bear the trace printing logic.
>
> Say if you have good UT coverage, is it necessary to have so many traces (or
> is it the best way to spy on the code under test)? And if the traces are for
> higher level logic, then it should be tested separately.
>
> terry
>
> > > > twitter:http://twitter.com/terryyin-Hidequoted text -
>
> > > > - Show quoted text -- Hide quoted text -
>
> > > - Show quoted text -
>
> --
> -terry
> -------------------------
> Blog:http://terryyin.blogbus.com/
> twitter:http://twitter.com/terryyin- Hide quoted text -

A. Robert S.

unread,
Sep 15, 2011, 9:06:56 AM9/15/11
to cpputest
mea culpa.

I found I did something terribly evil.

I neglected to call

mock().clear();

in my teardown code.

Now I can set about finishing unit tests for my trace_print() mock....

Robert

Terry Yin

unread,
Sep 15, 2011, 9:27:53 AM9/15/11
to cppu...@googlegroups.com
Hi,

Traces in code are supposed to be the good points for spying on the code. The problem is that they were not designed so. And when it's just a noise, perhaps you can just ignore it.

When testing legacy code in the way the legacy code designed it, it's a duplication of it's evil. Only makes things worse.

terry

Bas Vodde

unread,
Sep 17, 2011, 10:55:09 PM9/17/11
to cppu...@googlegroups.com

Hola Robert,

Sorry for the late reply!

I tend to test the formatted string precisely for the reason you mention. When I chose to have a formatted string, I'd like to actually let it be formatted accurately. So, I'll have *one* test that checks the printout using the resulting format. If the printout isn't important, I wouldn't use it in more than one test, so that I don't have duplication and becomes maintainable. That way, if someone changes the format he'd need to change one test, which I think is actually good :)

I addition to that, using the formatted string also makes your test a lot more readable.

Bas

Bas Vodde

unread,
Sep 17, 2011, 10:57:21 PM9/17/11
to cppu...@googlegroups.com

Hi,

Wow, you even want to check the types :) I'm impressed :P

The code looks pretty good. Did it eventually did what you wanted?

Bas

Bas Vodde

unread,
Sep 17, 2011, 11:01:51 PM9/17/11
to cppu...@googlegroups.com

Hiya,

> mea culpa.
>
> I found I did something terribly evil.

Well, terribly evil :) We get a bug report about that every now and then, it is fairly common.

You can actually do this automatically by installing the "MockSupportPlugin". You do this something like this (in main):

MockSupportPlugin mockSupport;
TestRegistry::getCurrentRegistry()->installPlugin(&mockSupport);

This will automatically do the checkExepctations and clean at the end of every test.

Bas

ps. design-wise you have to do this as the CppUTest base doesn't know anything about the mocking framework, so you'll need to tell it to clean up the mocks after teardown :)

A. Robert S.

unread,
Oct 5, 2011, 11:55:44 AM10/5/11
to cpputest
Yes, Bas, you are perfectly right about the plug-in. We will be using
MockSupport a lot, so this definitely looks like a very good idea.

And I am happy to say that my test code does its job very well now
that the leaks are fixed ;-)

Thank you so much for your support.

Robert
Reply all
Reply to author
Forward
0 new messages