Linking in test libraries with CppUnit
Asked Answered
L

3

6

I'm setting up a bunch of unit tests using CppUnit but am having the problem that none of the tests are being run. The project is divided up into several small libraries and I planned on dividing the unit test classes up the same way and then linking them all into a single test program. The problem is, then the test classes are in their own libraries, they don't get linked into the main test program unless I explicitly call them, i.e. I have to put in

runner.addTest( TestClass::suite() );

individually for each test class and can't use the TestFactoryRegistry's makeTests() method to get the list of tests. If I just compile them all together in the top directory the makeTests() method works fine but I don't want to have all the test classes in one location if I can help it.

The CppUnit documentation gives the following little hint

Linking problem when using Helper macros ?

When you create a project and write its unit test suites, the work is made easier through the use of the so-called helper macros : CPPUNIT_TEST_SUITE_NAMED_REGISTRATION, CPPUNIT_REGISTRY_ADD and CPPUNIT_REGISTRY_ADD_TO_DEFAULT. The problem is that if you use those macros in the source code file of a TestFixture class (say MyTest as an example), and if you use a line like this one

runner.addTest( CppUnit::TestFactoryRegistry::getRegistry().makeTest()

);

in your main() function in file main.cpp, there will have no test run at all !

The reason is simply that the link stage, one of the step of the build process, do not insert the object files (.obj or .o files) in the final executable if there is no undefined symbol in your main.cpp.

That way, the object code which contains the AutoRegister static variables instantiation is not part of the final executable and is not able to insert oneself into the runner in the main() function.

You have to create an undefined symbol in main.cpp so that the mytest.o file is integrated with main.o into the final executable.

Trick committed by Michel Nolard

but doesn't say how to make this work and I'm just dense enough not to be able to figure it out myself or find an example on-line.

Now I could just make a separate executable test for each library, and in the end I may go that way, but I wanted to try to get this working first so I just had one single test program to run to test the whole thing. Any ideas/examples of how to get this to work?

Lang answered 19/6, 2009 at 3:14 Comment(0)
E
1

By adding an undefined symbol to main, he just means create any random external symbol to force the linker into searching your external libraries that contain the test code.

For example, assuming two test libraries fred and barney, in fredTestLib.cpp you'd just add this line:

int fredDummyInt = 0; // declare a unique symbol for the linker to resolve

and in barneyTestLib.cpp, you'd add a similar line:

int barneyDummyInt = 0; // a different unique symbol for the linker to resolve

You could compile each library separately in different steps. In the main test program, you then force the linker to resolve them. So add these lines to main.cpp:

extern int fredDummyInt;
extern int barneyDummyInt;
...
main () {
    ...
    fredDummyInt++; // give the linker some symbols to resolve
    barneyDummyInt++;
    ...

The idea (according to what the author of the trick above is saying) is that because the linker is already searching fredTest.lib for fredDummyInt, it will also find and resolve your automatically registered tests.

Note: I have not tried this to see if it works! I'm just answering your question about externals.

Another approach to consider would be to create your tests in DLLs, and use LoadLibrary() to explicitly bring them in to run. For overkill, if you use the MfcUi::TestRunner, you could probably build a little drop-down GUI thing that lets you pick the library to load, loads it, then displays the tests to run in that library, then runs them.

Eigenfunction answered 24/6, 2009 at 14:36 Comment(2)
It almost worked how I wanted it to. Adding this in caused the registry system to pick up the tests in the file that contained the external variable but not from all the tests in the library. There are about a dozen classes (each in their own file) in the library and it only picked up the tests for one the variable was in. This probably actually the correct behavior but doesn't buy me anything over including each class individually. I was so hoping this would work.Lang
Well, you are trying to get "magic" results without code, and while I commend you on your attempt, at some point we all have to write code to get the jobs done. I fully agree that self-maintaining code is the best kind (the stuff where it "just works") but you're looking to go beyond the boundary of a single module here, and that's where it has to get tricky. How should the linker know when to stop bringing in new libraries? Should it search your whole hard drive? Current folder? Sub folders? Path folders? There have to be rational limits, and beyond those you have to be specific.Eigenfunction
U
0

The solution for this problem is rather simple as statet before (but may not be very elegant). For each TestFixture that is located in an external library you have to add the following two lines of code in the main module

#include <CppUnitTestFixtureExample.h>
CppUnitTestFixtureExample Test1;

It creates an unused dummy variable that is not used, it just forces the linker to link the test fixture. Now the test runner that is located in the main module is able to run the test.

Umbilical answered 25/1, 2012 at 14:9 Comment(0)
W
0

I realize this post is now quite old, but for anyone else who comes across it: One way to address this w/out having references in your code is to instruct (force) the linker to include the entire static library in the binary. Details area available from the gcc & ld man pages, and this post covers it too: How to force gcc to link unreferenced, static C++ objects from a library

Per ld's man page, it's important to consider explicitly turning the option off (also shown in one of the examples above).

Widespread answered 15/1, 2013 at 21:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.