Do you know the TIOBE survey? Every month it shows the usage of programming languages worldwide. And coming in first place this month is… C!
That’s right. There’s so much development still being done in C, where performance and program size matters, and the object orientation of C++ is a hindrance. Plus, we’re talking about so many new devices that C is their native language (apart from assembler).
With so many programs written in C, there’s obviously a need for unit testing. And where unit testing goes, mocking follows.
So here’s a reminder, how you can simply mock global C methods and C functions with Isolator++.
Here’s the code-under-test. It’s a method called SafeDelete, and it looks for a file before deleting it, using the global functions fopen, fclose and remove:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
void SafeDelete(const char* filename) { FILE* fp = fopen(filename, "r"); if (fp == NULL) { throw ("File does not exist"); } else { fclose(fp); remove (filename); } } |
As we all know, messing with files in a unit test is a no-no. The test takes a long time to finish, and we also need to take care of setup and clean up of files. So we want to mock the C calls. Here’s the first test, where we check that if the file is not there, we expect an exception to be thrown:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
TEST_F(FileHandlerTests, SafeDelete_NoFile_ExceptionThrown) { FAKE_GLOBAL(fopen); WHEN_CALLED(fopen(_,_)).ReturnPtr(NULL); FileHandler* handler = new FileHandler(); bool result = false; try { handler->SafeDelete("AnyFile"); } catch (char* exception) { if (strcmp(exception, "File does not exist") ==0) result = true; } delete (handler); ASSERT_TRUE(result); } |
With the FAKE_GLOBAL macro, we tell Isolator++ to mock fopen. Then in the next line, we tell fopen to return NULL when it gets called. When it does, the exception gets thrown, and we can check the contained message.
1 |
For the next test, we’re going to check what happens when the file is there. If a file exists, fopen need to return a non-NULL value. Then, we need to mock also the calls for fclose and remove, so we won’t have any nasty side effects or unforeseen exceptions.
1 2 3 4 5 6 7 |
TEST_F(FileHandlerTests, SafeDelete_ThereIsFile_RemoveWasCalled) { FAKE_GLOBAL(fopen); FAKE_GLOBAL(fclose); FAKE_GLOBAL(remove); FileHandler* handler = new FileHandler(); |
1 2 3 4 5 |
handler->SafeDelete("AnyFile"); delete(handler); ASSERT_WAS_CALLED(remove(_)); } |
Two things to point. The
1 |
SafeDelete |
method does not return a value, so our pass/fail criteria is that
1 |
remove |
was actually called
1 |
. |
In this example, I’m using the
1 |
ASSERT_WAS_CALLED |
macro with
1 |
_ macro |
as an argument to specify I don’t care about the arguments at runtime. Second, you’ll note that in this test I’m not setting any behavior on
1 |
fopen. fopen |
returns a pointer, and Isolator++ returns automatically an allocated pointer. This is enough to satisfy the
1 |
if-NULL |
check.Couldn’t get any simpler, can it? So the next time you bump into a C function that hampers your tests, Isolator++ can get you out of that jam. Mock C Functions with ease. If you’d like to know more about mocking in C and C++ we’re running a webinar on the topic. Register now!
1 |