google test with static libraries in msvc

Using google test with tests in static libraries under msvc has historically been a pain.
Often you would be left scratching your head wondering why some tests didnt run. This entry presents a tool that ensures that all tests from your static library will get run by your test runner.

Disclaimer: I don’t actually recommend this approach.
You will have cleaner interfaces if your tests are written in your test runner program, which then links against your production code library.
Plus it wont be possible for test cases to sneak into the production app.
This was purely done as an exercise, but it may be of use to some people.

If you have ever tried to use gtest to test code in static libs you have probably run into this problem.
See the gtest wiki for an overview of the problem.
The wiki ends up suggesting that you don’t put tests in static libraries.

Generally id agree with that.
But if you have a lot of code that doesn’t have external linkage then testing that code can only be done indirectly.
Sure, this may be a sign that either
* it hasnt been designed for testability
* the author hasent followed TDD
* havent broken the module down enough. (SRP)

But I still found myself wondering “why cant I put a test case in a static library?”.
Clearly the fact that its on gtests wiki means that other people wonder too.

Its common for people to put eunit tests at the bottom of their erlang modules and similar practices exist in other languages. They arent best practices. But if its so common in other languages why does it suck so much in msvc?

A walk through of the problem

The common idiom for using gtest (and other test frameworks) is to have a library where you put your code, a minimal program that uses that code, and a program that acts as a test runner. This is what im going to do in this article.

For this example we would be writing our tests in our static library.
(Perhaps we are testing a piece of code with internal linkage.)

// internal linkage function we are testing
static int plus(int a, int b) { return a+b; }
// test case
TEST(MathTest, TwoPlusTwoEqualsFour) {
	EXPECT_EQ(plus(2,2), 4);
}

Compile it. Everythings fine and we get our .lib file.

Then we have our test runner. It links against our lib and gtest

#include <gtest/gtest.h>
 
int main(int argc, char **argv) {
	::testing::InitGoogleTest(&argc, argv);
	return RUN_ALL_TESTS();
}

Time to run it and see our test passing.
But wait… we see this instead

What happened?

The msvc linker does not link an obj file from a static library into the main program unless there is an unresolved symbol in the main program that resolves to that obj.
This is fair enough. You dont want code in your exe that you dont need.
But heres the kicker: static initialization code that exists in an obj wont cause it to be linked in either.
gtest works by constructing classes that are initialized and linked into the test framework during static initialization.
GCC has a –whole-archive option, which can be used to link in everything, but msvc doesnt have anything like this.

Our test runner doesnt refer to ANY code in the static library, so our test doesnt get linked in.

The solution

heh. solution. get it? we are talking about visual studios… oh nevermind…

The solution is gen_msvc_test_header.py.
You can find it here.

If you have a static library with tests in it you can generate a header file which can be included in your test runner that will force the tests to run.

run like so:

python gen_msvc_test_header.py mylib.lib generated_test_syms.h

then have your test runner include the header file

#include <gtest/gtest.h>
#include <generated_test_syms.h>
 
int main(int argc, char **argv) {
	::testing::InitGoogleTest(&argc, argv);
	return RUN_ALL_TESTS();
}

compile and run and:

It works!

How it works

This python script runs dumpbin against the lib, it then runs a regex over the output to pull out symbols that match the constructors for gtests generated classes.
Once we have that its easy to emit a header file that forces the linker to include a reference to that symbol.

here is the header we generated above

#ifndef generated_5312bcde_fd17_4e3b_bbca_99f20116304a
#define generated_5312bcde_fd17_4e3b_bbca_99f20116304a
// Generated by gen_msvc_test_header at 2011-02-10T04:22:47.397000
// do not modify 
 
#pragma comment(linker, "/include:??0MathTest_TwoPlusTwoEqualsFour_Test@@QAE@XZ")
 
#endif // generated_5312bcde_fd17_4e3b_bbca_99f20116304a

If you were going to actually use this I suggest you set it as a post build step for your static lib.

This approach can also be used for other cases where you rely on static initializers being run, but you are using static libraries.

  1. Olivier Henley

    Maybe giving a try to another language would help.

    http://dlang.org/unittest.html

    My two cents 😉

Leave a Comment


NOTE - You can use these HTML tags and attributes:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>