Stubbing/mocking stdio functions

3,232 views
Skip to first unread message

Greg B

unread,
Sep 23, 2013, 8:59:11 PM9/23/13
to cppu...@googlegroups.com
Hi,

I've been using CppUTest for a while. Pretty comfortable with it, but now trying to test a logging class that makes calls directly to stdio functions (fopen, fclose, fread, fwrite and more).

Unlike other POSIX calls (pthreads for example) it seems I can't just stub these out because CppUTest also calls them. I am pretty sure it would be considered bad form to have the test cases actually create files on disk? So I think it would be recommended that I should stub them out, and keep the testing quite synthetic. So would the recommended or conventional approach be to make a thin wrapper around the stdio functions, and have this logging class go through this wrapper rather than directly to the stdio functions? Thus creating a link seam (and having to change the production code)?

Terry Yin

unread,
Sep 23, 2013, 9:53:11 PM9/23/13
to cppu...@googlegroups.com
Hi,

Yes, you create and use a wrapper. And if it's C++, you should "insert" the io stream into the function, therefore you don't need any linker stub.

You can also do this:

TEST_GROUP(TryOutput)
{
    FILE * saved;
    void setup() {
        saved = stdout;
    }
    void teardown() {
        stdout = saved;
    }
};

TEST(TryOutput, mock_printf)
{
    FILE * fakeOut = fopen ("standard-output-file", "rw");
    stdout = fakeOut;
    printf("haha");
    char buf[100];
    STRCMP_EQUAL("don't haha!", fgets(buf, 99, fakeOut));
}

This works and fails gracefully on my Mac. Guess on some other system you'll need to use freopen or something else to redirect the stdout.

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.

Terry Yin

unread,
Sep 23, 2013, 9:59:12 PM9/23/13
to cppu...@googlegroups.com
Hi,

Sorry, it didn't fail gracefully enough, the error message wasn't printed out. I think this is a *BUG* of cpputest.

And a simpler version could be:
TEST_GROUP(TryOutput)
{
};

TEST(TryOutput, mock_printf)
{
    FILE * fakeOut = fopen ("standard-output-file", "rw");
    UT_PTR_SET(stdout, fakeOut);
    printf("haha");
    char buf[100];
    STRCMP_EQUAL("don't haha!", fgets(buf, 99, fakeOut));

}

I'm going to create an issue for this.

br, Terry

Greg B

unread,
Sep 23, 2013, 11:25:41 PM9/23/13
to cppu...@googlegroups.com
Thanks for the very quick reply Terry.

It is C++, but I'm not quite following your answer.

Let's say I make a static class as a wrapper. With the genius name of Wstdio (W for wrapper). Then I change the logging class (the CUT) to call Wstdio::fopen() instead of fopen() directly. And in my tests, I can replace the production Wstdio class with a test stub class, with all the mocking malarkey. That was my first thought anyway. Not sure it's the right approach.

I'm not sure I know what you mean by insert the io stream. The logging class at the moment is initialized with a directory path it can use to create log files, but it opens/creates its own files. The API doesn't allow me to pass in a stream for it to write its log output to. It manages its own collection of log files.

I'm also not sure I understand your example code - what is the CUT? stdio functions? I want to test that this legacy logging class makes correct calls to stdio functions. And be able to make sure it handles errors from stdio functions.

Not sure if we're talking at cross purposes.

Thanks,
Greg

Terry Yin

unread,
Sep 24, 2013, 1:24:36 AM9/24/13
to cppu...@googlegroups.com
Greg,

Sorry for the confuse, I answered the wrong question. I thought you were asking how to stub printf...

If you want to mock fopen, fwrite, etc, you might try LD_PRELOAD to stub out these standard functions.

Alternatively, you can do this:
Declare this in a header file:
extern FILE *(*fopen_ptr)(const char *, const char *);
And then define this in a source file:
FILE *(*fopen_ptr)(const char *, const char *) = fopen;
Then replace all the places that's using fopen with fopen_ptr, and now your legacy code should behave just the same as before.

In your test:
FILE * mock_fopen(const char * filename, const char * x) {
return (FILE *) mock().actualCall("fopen").returnValue().getPointerValue();
}

TEST(MockDocumentation, otherMockSupport)
{
UT_PTR_SET(fopen_ptr, mock_fopen);
call_CUT();
}

Hopefully, I'm answering the right question this time:-)

br, Terry

Greg B

unread,
Sep 24, 2013, 4:19:21 AM9/24/13
to cppu...@googlegroups.com
Thanks Terry. That makes much more sense. :)

The LD_PRELOAD would break CppUtest I think, because CppUTest calls the stdio functions, no?

Anyway, the main thing I was driving towards was whether it is seriously uncool to have the test cases writing files to disk (answer "yes" I think), and therefore whether I need to mock/stub the stdio functions. I think I prefer linker stubs to function pointers, but it's six of one, half a dozen of the other.

Bas Vodde

unread,
Sep 24, 2013, 4:26:30 AM9/24/13
to cppu...@googlegroups.com

Hi Greg,

I personally always use function pointer stubbing for C library functionality as linked stubbing C library functionality has all kinds of site effects :)

Likewise for pthreads :)

Years ago I did a linked stub for pthreads library only to discover (after a day of debuggin) that the implementation of the C++ keyword throw used the pthreads library and was therefore causing an assert ;)

Bas

Terry Yin

unread,
Sep 24, 2013, 4:32:37 AM9/24/13
to cppu...@googlegroups.com
Unit tests that operating file can be really slow, and file is 'global' and hard dependency, so not cool at all. I would say a handful of 'unit tests' that are talking to files is still acceptable...

One idea just came to my mind (never tried it myself):
you can mock fopen *only*, and let the mocked fopen return a handle to a memory file, then you can spy on the memory file. No need to mock fgets, fwrite, whatsoever.

Greg B

unread,
Sep 25, 2013, 1:08:04 AM9/25/13
to cppu...@googlegroups.com
Hi Bas,

Nice to talk to the boss. :)

I have bought into CppUTest, and James' book, in a major way. However, I have got to say I'm not a big fan of function pointer stubbing. I'm Ok with the basic principle of it, but the idea of putting them on the server side, as a global variables, seems to fly a bit in the face of data encapsulation and SOLID design principles. I would prefer the function pointers to be private data on the client side, with the pointers installed at run time (dependency injection), through the client's API.

So I am all for the loose coupling that function pointers can provide, but I think the client should be aware of the loose coupling. The onus should be on the client, not the server. Well that's what I reckon anyway. :)

Anyway, you guys rock. Really, I think it's great how you, Terry and James get right in at the coalface here, answering questions directly, and in a very timely fashion.

Regards,
Greg

Greg B

unread,
Sep 25, 2013, 1:09:36 AM9/25/13
to cppu...@googlegroups.com
Nice idea about fopen. I'm almost paralyzed by the wealth of options. :)

A. Robert S.

unread,
Sep 25, 2013, 4:08:29 AM9/25/13
to cppu...@googlegroups.com
Hi Greg,
 
Unfortunately, I can't say much about stubbing fopen() etc. as I haven'd done this myself. I found it a lot easier to stub <iostream>. It was actually as easy as making my production code take an <ostream> instead of an <ofstream> via constructor.
 
However, I use dependency injection on a regular basis for *any* production code functions written in C (no stdlib functionality). This lets each test decide whether to work with a mock, a handwritten stub, or a simple dummy (the dummy being the default). We keep the function pointers in a struct. Note that the production code does not use or need know about these function pointers. We even managed to swap in the original function, but that is a bit of a hack ;-).
 
If we had to resolve this at link time, we would have to use all mocks, or all stubs, or always the same combination thereof, for any given module ("class") we need to stub out, or God forbid, have different sets of the same fakes for different tests of other modules.... you get the picture.
 
Robert

Bas Vodde

unread,
Sep 25, 2013, 5:24:36 AM9/25/13
to cppu...@googlegroups.com

Hiya Greg,

> Nice to talk to the boss. :)

*grin* Well, I'm not the boss :P

>
> I have bought into CppUTest, and James' book, in a major way. However, I have got to say I'm not a big fan of function pointer stubbing. I'm Ok with the basic principle of it, but the idea of putting them on the server side, as a global variables, seems to fly a bit in the face of data encapsulation and SOLID design principles. I would prefer the function pointers to be private data on the client side, with the pointers installed at run time (dependency injection), through the client's API.

Yeah.. I used to think that too :)

Nowadays, I do use function pointer replacement quite a lot more when working with e.g. C library as it is so convenient (especially when using UT_PTR_SET). I just look at it as putting a *very thin* wrapper between the 3rd part and your code and try not to think them of function pointers :)

> So I am all for the loose coupling that function pointers can provide, but I think the client should be aware of the loose coupling. The onus should be on the client, not the server. Well that's what I reckon anyway. :)

Well, for function pointers, I really only use them for stubbing in tests, so really don't want them in the interface as that would complicate all code.

> Anyway, you guys rock. Really, I think it's great how you, Terry and James get right in at the coalface here, answering questions directly, and in a very timely fashion.

Oh, well, you are lucky that we are not all doing other things at the moment :P

Bas

>
> Regards,
> Greg
>
>
> On Tuesday, September 24, 2013 4:26:30 PM UTC+8, Bas Vodde wrote:
>
> Hi Greg,
>
> I personally always use function pointer stubbing for C library functionality as linked stubbing C library functionality has all kinds of site effects :)
>
> Likewise for pthreads :)
>
> Years ago I did a linked stub for pthreads library only to discover (after a day of debuggin) that the implementation of the C++ keyword throw used the pthreads library and was therefore causing an assert ;)
>
> Bas
>
>

Knut Aksel Røysland

unread,
Sep 25, 2013, 6:24:37 AM9/25/13
to cppu...@googlegroups.com
Hi Greg,

Other solutions already discussed is probably to be preferred. However, if code under test does use fopen() and you do not want to change that, and you are using GNU tool-chain, you could try with a link-time injected fopen() wrapper that can be instructed to call either a mock implementation or the real implementation, using dlsym(). Attached file illustrates how it might be done. Link with libdl (-ldl).

The decision of whether to use the mock version or the real version, could be based on anything, including the file path being opened, so that certain paths will be mocked while other paths will lead to real file system files.

I used fmemopen() here for simplicity. I recommend using fopencookie() if you want full control over your FILE mock. Other FILE functions, like fread(), fwrite(), fseek(), and fclose(), will not have to be mocked at all, since they will correctly delegate to the fopencookie()-based mock.

--
Sincerely,
Knut Aksel Røysland

2013/9/24 Greg B <gr...@footyforecaster.com>
Hi,

I've been using CppUTest for a while. Pretty comfortable with it, but now trying to test a logging class that makes calls directly to stdio functions (fopen, fclose, fread, fwrite and more).

Unlike other POSIX calls (pthreads for example) it seems I can't just stub these out because CppUTest also calls them. I am pretty sure it would be considered bad form to have the test cases actually create files on disk? So I think it would be recommended that I should stub them out, and keep the testing quite synthetic. So would the recommended or conventional approach be to make a thin wrapper around the stdio functions, and have this logging class go through this wrapper rather than directly to the stdio functions? Thus creating a link seam (and having to change the production code)?

--
fopen_mocking.c
Reply all
Reply to author
Forward
0 new messages