Unit Testing C++ with Google Test

Unit testing C++ applications is not exactly easy. Without any embeddable metadata, the actual process of running a unit test has to be defined explicitly in code. Luckily, frameworks such as Google Test provide comprehensive support for this. Let’s take a look.

Download and Build

Google Test is not header-only: there are libraries to build. So, as a Visual Studio user, you have essentially two options.

Option 1 (probably the easiest): Just install Google Test from Nuget:

This sets everything up, but your mileage may vary: the package comes with pre-build binaries that may not target the compiler and bitness you want. So the other option is…

Option 2 (more effort required): Download and build Google Test in some local directory (e.g., c:\gtest). Now, lucky for us, Google Test comes with a set of Visual Studio projects/solutions ready for building the binaries. You’ll find those in \msvc. Now is a chance for you to open up the solution (e.g., gtest.sln), configure things how you want, then build it and copy the library files and EXEs to some convenient location (e.g., \gtest\lib).

The icing on the cake is you can now open up the %LOCALAPPDATA%\Microsoft\MSBuild\v4.0 folder and edit the .user.props files to always include the Google Test paths. Or use Visual Studio’s own Property Manager. This way you’ll always have the directories available, though the library to link against depends on whether you’re building a Debug or Release version.

How It Works

Let’s state the obvious first: Google Test requires you to build an executable. It’s a command-line app, and when you run it, it runs your tests. Even makes them nice and colorful:

So, it is entirely possible to keep rebuilding and running the EXEs, visually inspect the results and fix when necessary. Of course, this works when you have a few tests: if you have hundreds, well… this is where you need tool support.

At any rate, the first thing you need to do is

  • Add the include header
  • Reference the appropriate libraries. If you are using the NuGet package, you don’t have to do this, but otherwise you need to reference gtest.lib for the Release build and gtestd.lib for Debug.

So, in order to run the tests, Google Test needs you to implement the main entrypoint with code similar to:

The call to InitGoogleTest() parses command-line arguments: you can specify your own arguments. To find out which arguments Google Test supports, just run your EXE with the --help flag.

What is a Test?

In its simplest form, a test is just a chunk of code that checks if some condition holds. If it holds, the test passes; if it doesn’t, the test fails.

There are many ways to test conditions. The simplest varieties are EXPECT_TRUE() and EXPECT_FALSE, which can be used as follows:

You’ll notice that the above expectation is wrapped in a TEST() macro. This macro is necessary to tell Google Test that you are, in fact, writing a test. A single test can contain more than once statement, of course.

A slightly more useful variety is EXPECT_EQ(), which tests that the second argument is the same as the first, for example

So the above states that 4 should be the result of adding 2 and 2. I also use the << operator to give a human-readable message to whoever is running the test in case it fails.

Now, EXPECT_EQ does not stop execution. If you have several EXPECT_EQ calls in one test, they will all run. If you want the first test comparison to stop execution if it fails, use ASSERT_EQ instead.

Some refinements of EXPECT_EQ (and ASSERT_, by analogy) include comparisons (e.g., EXPECT_LT is a less-than check), comparisons of floating-point numbers (EXPECT_FLOAT_EQ and similar), and many more.

At any rate, your TEST() is now runnable. Its location isn’t important so long as it ends up being part of the build. If you want to figure out what tests you’ve got written, just use R++’s Unit Test Explorer, which will locate and list all the unit tests in the project.

Test Fixtures

Whenever you’re testing stuff, you are probably testing a class or set of classes. Setting up and destroying them on every test might be painful and this is what test fixtures are for.

A test fixture is a class that inherits from ::testing::Test and whose internal state is accessible to tests that use it. This is a critical distinction that might be a bit difficult to understand for users of frameworks in other languages. Essentially, instead of being part of the test fixture class, the tests related to a fixture are external. Yeah, it’s weird but that’s what we have.

A test fixture can define set-up and tear-down actions in either SetUp()/TearDown() or in the constructor and destructor. For example, if I want to test a BankAccount, my test fixture might appear as:

But the test fixture is not an actual test: it’s just rules for setting up and destroying objects that you need for testing. (Of course, we could have used a unique_ptr but I wanted to show a destructor, so there.) Anyways, the actual test now uses a TEST_F instead of the usual TEST, and its first argument is the fixture:

With ReSharper C++, you can run this test by pressing Alt+Enter and choosing Run:

Choosing this option causes the program to be compiled and the test executed. You can get a visual read-out on the state of your tests in the Unit Test Sessions window:

How Do I Test Lots of Data?

Data-driven tests? I’m glad you asked, because Google Test has something called parameterized tests. Essentially, you can just set up a set of values and feed them all consecutively into a test. First of all, set up your structure for running the test:

The above is a statement of before-and-after states for a bank account. We are testing the process of withdrawing money. Oh, notice how I used R++ to generate the operator<< pretty-print: this is necessary because Google Test has no idea how to print account_state objects to the console… and we kind of need this for good-looking tests.

Now comes the tricky part: we want to reuse our previous BankAccountTest fixture but, at the same time we want to use account_state instances to initialize the balance. Here’s how it’s done:

The magic sauce here is in the WithParamInterface<> class as well as the GetParam() function, which yields the parameter being used in the current test case. Finally, we can write the test itself…

Notice the use of TEST_P here. The rest is pretty much the same: we get the test values with GetParam(), extract what we need, perform the test and check not one but two values. So finally, the icing on the cake is in us defining the test cases for this test. Using the magic of uniform initialization we can write it as follows:

When working with parameterized tests, you run them just as you would run a single test or a test based on a test fixture. The difference is in the output: each case takes up a separate line in the tree of test cases:

There’s More!

Google Test is a really big and comprehensive framework. Together with its sister, Google Mock, they provide ample possibilities for unit testing. Check out the advanced guide to find out more about sophisticated Google Test practices.

And here’s a video illustrating the story described above.

Hey, did I mention that ReSharper C++ supports Google Test? Well, it does. So check it out if you haven’t already. ■

This entry was posted in ReSharper C++ Tips & Tricks and tagged , , . Bookmark the permalink.

16 Responses to Unit Testing C++ with Google Test

  1. Victor says:

    Another (easier) option to set up Google Test is to use the fused version which only requires adding one file, gtest-all.cc, to the build. Also you don’t have to implement main yourself, as gtest_main.cc already provides the implementation.

  2. Harry says:

    This seem to fail with visual studio 2015. It seem that nuget is doing something wrong.

  3. ddb says:

    gtest prints some important information to the console for failing cases e.g.:
    ASSERT_EQ(expected, value) << "this line will be printed to console, it includes some important info";

    1. gtest will print the expected value and the value that did not match
    2. gtest will print the text, after ASSERT macro that suppose to explain even better the failing case.

    My question is, where can I find the gtest console output, when using Resharper C++ unittest environment?
    thank you.

    • Igor Akhmetov says:

      All test output is saved and should be accessible in the output pane of the “Unit Tests Sessions” window – just make sure that the pane is visible (there’s the “Show Output” toolbar button that controls visibility of the pane).

      There are a couple of known issues:
      1) Output of 64-bit executables is not logged while they are debugged.
      2) Output pane sometimes does not get properly updated between the runs (https://youtrack.jetbrains.com/issue/RSRP-447841) – we hope to fix this in ReSharper 10.

      If you still can’t see the output and the issue is not covered by one of the former cases, we’d love to know how to reproduce it – please create an issue on our tracker (https://youtrack.jetbrains.com/issues?q=%23RSCPP) with the required steps.

  4. Guglielmo says:

    It seems that x64 is not supported, is it?

    • Igor Akhmetov says:

      x64 binaries are supported with one caveat – after you debug some unit tests, their test status will be inconclusive as ReSharper C++ is not able to intercept the output of a x64 binary under debugger.

  5. hung tran says:

    hi,
    I am trying to setup and do one simple unit test using GoogleTest but always struggling in Visual Studio.
    Do you have any reference to the fused version as far as setting it up .
    thank you

    • Igor Akhmetov says:

      Hung, you have to run the googletest/scripts/fuse_gtest_files.py script from the Google test distribution to obtain the fused sources – then just add them to your solution.

  6. Phil Miller says:

    Do you have plans to extend these capabilities to the Boost.Test framework or will this remain a google-test only capability?

    • Igor Akhmetov says:

      Phil, ReSharper C++ 2016.1 added support for running tests written using the Boost.Test framework.

Leave a Reply

Your email address will not be published. Required fields are marked *