rtur
rtur

Reputation: 338

Unit tests : cohabitation of the production-code and the mocked implementation

I know about the basics about test doubles, mocking, etc. but I'm having problems to test the following

void funA(void) {
    /* do some stuff, using mocked functions from 3rd party library */
}

I've written the unit tests for funA(), checking the good functions were called (using their mocked implementation). So far, the mocked functions are library functions. This is not my code. I don't want to test their original implementation.

Now, I want to test this function

void funB(void) {
    /* do some complicated stuff, and call `funA()` on some situations */
}

How can I be sure my funA function was called from funB? I can't add a fake implementation to funA, I need its production code so it can be tested.

What I am doing now is making sure the mocks that funA is calling are as I expect them to be. But it's not a good method, because it's like I'm testing funA all over again, when I just want to make sure funB does its job.

Upvotes: 1

Views: 188

Answers (1)

rtur
rtur

Reputation: 338

After discussing it (see the comments of the original question), and having a brief forum exchange with James Grenning (one of the author of CppUTest), the main solutions are the following:

  • Having different test builds for funA() and funB()
  • Using function pointers to dynamically change the behaviour

I'm not a huge fan of either of the solutions, but it feels like I can't do much more in C. I will eventually go for the multiple binaries solution.

For reference, here is the answer of James Grennings:

You may want to mock A() when testing B().

for example

If I have a message_dispatcher() that reads a command from a serial port via getline(), and getline() uses getc() and getc() uses IORead and IOWrite. I could mock IORead and IOWrite and have a set of horrible tests to test the message_dispatcher().

Or I could test getc() with mock IORead() and IOWrite(), getline() with some kind of fake_getc(), and test message_dispatcher() with fake_getline(). If you only use linker substitution, you would need three tests builds. If you used function pointers you could do one test build. You can mix an match too.

getc() should be tested with link time mocks of IORead and IOWrite because your unit tests never want the real IORead and IOWrite for off-target tests (they may be needed of on-target tests, but those would be integration tests).

There are many possibilities. You could also have some other code getline() and feed it to the dispatcher, removing its dependencies.

Upvotes: 1

Related Questions