Google Test - Just what I needed at the right time
Introduction
I was looking for a unit test framework I could easily implement to drive some recent test driven development (TDD) work in C++. I am new to the more formal methodologies (TDD and BDD) where most of my unit testing previously was ad hoc and custom code — often in-source, macro and controlled through hard-coded #define statements.
Use of a framework in C++, similar to mocha in Javascript, was appealing to me and I hoped to learn a new approach I could apply in my research work. After searching on the wikipedia page here [1], I noticed that Google had an open-source library for C++ unit testing called Google test, or shortened to gtest. The Google Test library is based on the xUnit architecture which can be considered a TDD unit testing framework. The xUnit architecture consists of a test runner which is a separate executable that performs the testing.
Installation of the Google Test library
The Google Test library does not come pre-compiled for the Ubuntu 18.04 Linux box I am working on so I had to build the library first. I first installed the library. I have the basic gcc and cmake programs installed.
$ sudo apt install libgtest-dev
I then went to the installed source location and performed the cmake four-gun salute (may be different for your installation, check with your installed package)
$ mkdir build && cd build && cmake .. && make
Finally, I copied the generated .a files to the /usr/lib/ folder.
Adding Google Test to a cmake build
Using a test runner as a separate executable fits well with the cmake building process as a separate test target can be added with minimal effort. The CMakeLists.txt in the project root needed some extra lines,
# Setup testing
find_package(GTest REQUIRED)
include_directories(${GTEST_INCLUDE_DIRS})
add_executable(tests test/tests.cpp)
target_link_libraries(tests ${GTEST_LIBRARIES} pthread)
The Google Test framework also allowed me to pick the directory structure for the project rather been stuck with a preset format. I elected to put the test files in a separate directory where the directory structure looks like,
<project>
|- /src
|- /include
|- /test
|- /build
One main() to rule them all
In an executable target , only one main() entry point is allowed. One of the projects I was writing tests for is a simple program to remove comments from a file. No OOP or anything special. I could (should have) written it in python, but I needed something to work with the C++ TDD process — so here we are. Since I want to include the global namespace functions I wrote in the main .cpp file, I simply #include "decom.cpp" the main source file in the test runner tests.cpp file.
I wrote some simple tests, as described in the Google Test primer here [2]
Here is the initial tests.cpp file,
#define __XUNIT_TESTS_ENABLED
#include "recom.cpp"
#include <gtest/gtest.h>
bool False = false;
bool True = true;
TEST(fnRemoveComment, noComments) {
ASSERT_EQ("Line 1", removeComment("Line 1",False));
ASSERT_EQ("Line 2-3", removeComment("Line 2-3",False));
ASSERT_EQ("", removeComment("",False));
}
...
int main(int argc, char** argv) {
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
In the recom.cpp file I put a define exclusion guard around the main() function so that the test runner entry point is in the tests.cpp.
#ifndef __XUNIT_TESTS_ENABLED
int main(int argc, char** argv) {
...
}
#endif
It is a hack and there probably is a better way of doing it — but it worked in this case.
After running the test runner executable, a neat and orderly report is generated on stdout and an exit code (PASS or FAIL) can be captured to be used in scripted CI/CD workflows.
The tests are simple in that they check actual output to the expected output of function calls. More elaborate testing is available through the Google Test framework using fixtures and mocking behaviours. But for simple tests, the Google Test xUnit testing framework passed with flying colours.