Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Googletest Eclipse C++ : How to have both test and production executable?

I have a basic question regarding Googletest in Eclipse.

I am using the test-runner plug in to run the Googletests. But I need to specify a binary which runs my unit tests (of course that makes sense.)

The problem is that in my project I now have two main functions, one to run the actual program and one

int main(int argc, char** argv) {
  ::testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}

to run the google tests.

Each time I want to run one I comment the other out, which of course is stupid.

But what practice do you use to handle this situation?

like image 394
1v0 Avatar asked Jul 09 '15 17:07

1v0


1 Answers

Googletest C++ is a unit-testing framework. That means it is intended for testing implementations of C++ APIs. It isn't intended for testing programs.

For practical purposes a C++ API is what you get in a C++ header file. The implementation of such an an API might be:

  • Just the header file itself. (The implementation is entirely inline)
  • The header file plus a single C++ source file
  • The header file plus a bunch of C++ source files

To generalize, the implementation of a C++ API is a header file plus 0 or more source files.

Say your program my_prog invokes an API that you or your team have developed for managing gizmos. The implementation is something like:

gizmo.h
[gizmo_0.cpp,...gizmo_N.cpp]

where [...] means optionally ...

Maybe my_prog relies on other APIs for which you or your team are responsible, but we'll stick with just one. my_prog uses the gizmo API by:-

  • Using #include "gizmo.h" in some source files.
  • Compiling the [gizmo_0.cpp,...gizmo_N.cpp] source files, if any.
  • Linking the [gizmo_0.o,...gizmo_N.o] object files, if any.

(gizmo_0.obj, etc. if you're on Windows)

Testing your implementation of the gizmo API with Googletest is supposed to confirm that this implementation is correct, independently of my_prog or any other program that relies on it to manage gizmos. So incorporating the unit-testing of the implementation in the implementation of my_prog is misguided:-

Maybe your colleague writes another program that also needs to manage gizmos with this implementation. Maybe you write another one. Is whoever writes this other program supposed to repeat the process of incorporating gizmo unit-tests into it - The same ones? Different ones? - and making the program conditionally compile as either a gizmo test-harness or as whatever it's supposed to be in real life?

And how do you know that the gizmo implementation isn't somehow entangled with functionality that's unique to my_prog, or with the implementation of some other API that my_prog uses in the same way - so that when you or somebody else tries to reuse it in another program, it breaks or behaves wrongly?

No program that relies on this gizmo implementation is the place to put its unit-testing. Making my_prog conditionally compile different main functions so it can double as a unit-test harness for your gizmo library is similar to cutting a hole in the crotch of your jeans for your head to fit through.

The way you're supposed to unit-test the gizmo library is to write a program that is the test-harness for this library, and nothing else. This program, say gizmo_test, will use the gizmo API in just the same way as any other program would use it, but for the sole purpose of testing the gizmo library. All that gizmo_test will do is execute tests of the gizmo library, by invoking its API.

As a first approximation, the GoogleTest recipe for gizmo_test is:

Write a header file, gizmo_test.h

#include "gizmo.h" in it

#include <gtest/gtest.h> in it

Then write your Googletest test cases in it

Write the following source file gizmo_test.cpp

#include "gizmo_test.h"

int main(int argc, char** argv) {
  ::testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}

Create a project gizmo_test - in Eclipse or whatever development environment or build system you use - that builds the gizmo_test executable by:

  • Compiling the source files gizmo_test.cpp + [gizmo_0.cpp,...gizmo_N.cpp]
  • Linking the resulting object files gizmo_test.o + [gizmo_0.o,...gizmo_N.o], plus libgtest and any other libraries on which your gizmo library depends

You have two projects. The one that makes my_prog and the one that makes gizmo_test. In your development environment or build system, make the build of my_prog depend on the build of gizmo_test, so that when you change anything that affects the gizmo library and rebuild my_prog, gizmo_test gets rebuilt first.

That's a first approximation. Did you notice a while back that I started talking about your gizmo library? That's what you've got (or should have). In C++ and programming generally, the implementation of an an API is called a library.

And maybe you also noticed some fragility, inconvenience and wastage in the recipe for gizmo_test. You have the same set of gizmo source files [gizmo_0.cpp,...gizmo_N.cpp] in both projects. So you might edit, compile and link them differently in two projects. And they'll get compiled in both projects, either differently, which is wrong, or identically, which is pointless.

Of course if this set of source files is empty - the gizmo library is nothing but gizmo.h - there's no such problem. But if it's not empty, there is.

As you know, in C++ we don't use a library by building its source files in every program that uses it - not unless it's a header-only library. A library is built by itself into an object library (either static or dynamic), and to use it a program just includes the library's header file(s) and links the object library.

That's how a program should use your gizmo library too. So to the final approximation:-

  • Make a project libgizmo that builds a gizmo object library (static or dynamic, as you see fit).
  • Make a project gizmo_test as above, except that instead of compiling and linking [gizmo_0.cpp,...gizmo_N.cpp], it just links libgizmo, and make this project depend on the libgizmo project.
  • Make a project my_prog as you have it now, but instead of compiling and linking [gizmo_0.cpp,...gizmo_N.cpp], just link libgizmo, and make this project depend on the gizmo_test project.

So you have three projects by the time you build the first program that uses the gizmo library. Each subsequent program that uses the gizmo library needs one more project, like the my_prog project.

Googletest is designed for testing C++ libraries, and this is how you're supposed to use it.

Now I know nothing about your program or how you are currently deploying Googletest test cases in your project. Maybe there aren't any well-defined API implementations in it that those test cases are supposed to exercise, that you can factor out into free-standing libraries. Possibly that could be because your program is so very simple that unit-testing its "components" is non-applicable, and you'd be wiser just to write blackbox tests of the program. More likely it would be because you've so far failed to design a program architecture that is capable of being unit-tested. If that's what you find, you need to fix it, and then apply Googletest the right way. It will be worth the effort.

And in case it needs pointed out, unit-tests are not program tests, so as well as unit-testing any libraries your program relies on, if they are your responsibility, you also need blackbox tests of your program.

like image 125
Mike Kinghan Avatar answered Nov 04 '22 21:11

Mike Kinghan