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 againstLibraryA.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?
2 Answers 2
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.
-
Can you explain more about your first option? How do you do that?void.pointer– void.pointer2015年06月18日 11:33:53 +00:00Commented Jun 18, 2015 at 11:33
-
Thanks for the edit. What about symbols that are built into the lib that are used but don't need to be mocked?void.pointer– void.pointer2015年06月18日 13:39:26 +00:00Commented Jun 18, 2015 at 13:39
-
@void.pointer just ignore themBЈовић– BЈовић2015年06月23日 08:31:50 +00:00Commented Jun 23, 2015 at 8:31
Simple: mock libraries (mocking both .cc and .h files) and be prepared that the same library will be mocked in different ways for different tests.
Explore related questions
See similar questions with these tags.