C++ / Programming

Using googletest as unit test for C++

I love C++, but the lack of actual tooling is one of the most annoying things about it. luckily, there are many open source tools to bridge this gap. The googletest unit test suite is one of the most famous C++ testing tools. It’s open source, and developes by Google.

Installation options

You can install google suite in two ways, one is per project (which is what they recommend) and globally per machine, which is what lazy people (hello) do. The difference is the error sensitivity; It is possible, in theory, that a test will produce different results and therefore will fail under different versions. I find that hard to believe, and if such a failure is indeed found, the code should be fixed, unless it is an obvious failure within the tests suite, in which case, a simple upgrade will do the trick.

Installation

You’ll need cmake, and a decent c++11 and above compiler, not an issue these days. once you have downloaded the source code from github, you can run cmake and you’ll build the project. You can then install it, or use it locally.

Using google test

The First thing needed is the main test function, since it’s not the code main function, cmake (or a better equivalent) should definitely be used. The google test (gtest from now own) main function is fairly simple and goes like this:

#include<gtest/gtest.h> int main() { testing::InitGoogleTest(); return RUN_ALL_TESTS(); }
Code language: C++ (cpp)

I usually call my test main file, tests_main, to differentiate from the regular main. Now, how many main functions can you have in an executable? Right, 1. So, when linking this file, it’s going to go within it’s own execution file. Pretty good setup giving that it won’t be a part of the code you ship. The rest of the tests can (and should) reside in other files. You can still leave them within the same file as main, but it’s not recommended – you’ll add tests and you don’t want to have a giant file, or to mistakenly create another main function.

Writing a test

gtest is macro based, and a simple test will look like this:

#include<gtest/gtest.h> TEST(CounterTest,TestSettingValue) { Counter c; c.setValue(5); EXPECT_EQ(c.getValue(),5)<<"suppose to be 5"; }
Code language: C++ (cpp)

Within the TEST macro, you set the first and second parameters as you see fit. The custom is that the first parameter is the test suite name, and the second one is the specific test. In this case, I’m testing a Counter object (hence the CounterTest) and specifically if we value is being set correctly (TestSettingValue). The combination must be unique, again, this so called limitation is considered by me as a good design choice – if you name your tests properly, you won’t write then twice.

Useful macros

EXPECT_EQ checks values for equality, EXPECT_FALSE, EXPECT_TRUE check boolean values, EXPECT_GT and EXPECT_LT checks against bigger or smaller values, and EXPECT_LE and EXPECT_GE check for bigger or equal or smaller or equal. other interesting but less usual are EXPECT_DEATH, EXPECT_EXIT, and EXPECT_THROW which check for termination or exception throwing (the last one – is more useful than the others). In addition, gtest includes floating point tests as well. If you’ll replace the EXPECT with ASSERT, the suite will exits in case of a failure and will not continue. I don’t use this feature, as I consider any failure as a show stopper, but there is always a case. Last but not least are GTEST_SKIP_() and GTEST_SKIP() – to skip a test, one with a text message, the other without.

Linking and running

This has less to do with gtest itself and more with the way you construct your project; I love linked libraries. Static or dynamic are project considerations; However, linking the code base to one file and the presentation logic (even if it’s just a plain console) makes a lot of sense, and in this case, helps you test, as this CMakeLists.txt file shows:

cmake_minimum_required(VERSION 3.5 FATAL_ERROR) set(CMAKE_CXX_STANDARD 17) project(c14 LANGUAGES CXX) add_library( counter STATIC counter.cpp counter.h ) add_executable( tester tests_main.cpp sorter_test.cpp ) add_executable( runner main.cpp ) target_link_libraries(runner counter) target_link_libraries(tester gtest pthread counter)
Code language: CMake (cmake)

You can clearly see that the tests are compiled separately, as well as that the executables are separate to run. So, to run tests for this project, the command will be tester. Simple and elegant. Another thing showing here is that you need to include the pthread library (Linux, I didn’t try that under windows). Also, order matters. gtest should appear before pthread.

Setup and Tear-down

gtest is meant to be simple; However, some time we need to run a setup task before every test, which in that case a little bit of extra work is required:

Counter myCounter; // global for this unit // derived from gtest test class CounterTester : public testing::Test { private: /* data */ public: public: CounterTester(/* args */); ~CounterTester(); public: void SetUp() //called before every test { myCounter.setValue(5); } void TearDown() {} //called after every test }; CounterTester::CounterTester(/* args */) { } CounterTester::~CounterTester() { }
Code language: C++ (cpp)

After we setup the derived class, using it in a test is almost the same with one little difference:

TEST_F(CounterTester, TestInitial) { ASSERT_EQ(5,myCounter.getValue()); }
Code language: C++ (cpp)

In case you didn’t notice – it’s the _F and the end of the test macro. Now, if you need a massive initialization at the beginning of the test – just put it in the constructor. If you don’t like the global variable – you can use static class members.

Leave a Reply

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