C++ – How to setup build system for unit testing + mocking

build-systemccmakemockingunit testing

We have a legacy code base in entirely C++. Our build system is CMake. My first stab at unit testing was as follows:

  • Define a target (LibraryA). This library contains the code to test.
  • Define a unit test target per CPP test file in LibraryA being tested and link against LibraryA.lib.

Basically the source structure is like so:

LibraryA/
  Source/
    Utils/
      MyClass.cpp
      MyClass.hpp
      AnotherClass.cpp
      AnotherClass.hpp
    Network/
      Socket.cpp
      Socket.hpp
  Tests/
    Utils/
      TestMyClass.cpp
      TestAnotherClass.cpp

What we have here is a mirror structure of the Source directory inside Tests. This structure allows me to use include order trickery at the compiler command line level to prioritize where class headers are found for mocking purposes. Each Test*.cpp file under Tests results in 1 executable. So 1 executable test per class being tested.

The issue is that I'm linking against LibraryA.lib in my test executable. This results in ODR violation because if I want to mock the Socket class, the LibraryA.lib will have already compiled symbols into it. Now I can't mock it without duplicating symbols and the linker will complain.

Overall setting up unit test structure has been a giant pain from the build system perspective when it comes to mocking.

Is there a good solution to this problem? Is my approach to test structure completely wrong?

Best Answer

There are two options :

  • don't link Socket compiled binary into the libaryA, and compile libraryA with mocks. I think this is what you are trying to do : link time dependency injection.
  • use run-time dependency injection. Add interface for the Socket class, and use some kind of dependecy injection (factory, constructor or method), and in tests, instead of injecting the real Socket, inject the mock. This is even better, since you can test the calls to the mock.

The link time dependency injection (the first option), means that you inject the code during the linking time.

If Socket.h declares the Socker class, and Socket.cpp implements it, then add a header defining the mock class with the same name - Socket. It has to be in the same namespace as original Socket.

Then when you build the unit test, do not compile and link the Socket.cpp, you need to compile and link the mock class, and use it in the unit tests.