This tutorial assumes that you have a C++ compiler and build tools installed on your system.
Download GoogleTest: gtest-1.7.0.zip
GoogleTest:
Installation: install to /opt/gtest/...
unzip gtest-1.7.0.zip cd gtest-1.7.0 sudo mkdir /opt/gtest /opt/gtest/include /opt/gtest/lib ./configure --prefix=/opt/gtest make sudo cp -a include/gtest/ /opt/gtest/include sudo cp -a lib/.libs/* /opt/gtest/lib rm /opt/gtest/lib/libgtest.la rm /opt/gtest/lib/libgtest_main.la sudo cp -a lib/libgtest.la /opt/gtest/lib sudo cp -a lib/libgtest_main.la /opt/gtest/libNote: cp flag "-a" preserves symbolic links
You can also locate GoogleTest in global user directories /use/include and /usr/lib. I personally don't mix package controlled system software with non-package controlled software and thus I use /opt/gtest and not the system areas.
Google Test is a framework in which we write a unit test driver to call and test C++ class methods.
The C++ classes which will be tested by Google Test. The total application will not be tested although it is listed.
File: src/Addition.hpp#ifndef _ADDITION_HPP_ #define _ADDITION_HPP_ class Addition { public: static int twoValues(const int x, const int y); }; #endif
File: src/Addition.cpp
#include "Addition.h" int Addition::twoValues(const int x, const int y) { return x + y; }
File: src/Multiply.hpp
#ifndef _MULTIPLY_HPP_ #define _MULTIPLY_HPP_ class Multiply { public: static int twoValues(const int x, const int y); }; #endif
File: src/Multiply.cpp
#include "Multiply.h" int Multiply::twoValues(const int x, const int y) { return x * y; }
File: src/ExampleApp.cpp
#include "Addition.hpp" #include "Multiply.hpp" #include <stdio.h> int main() { int x = 4; int y = 5; int z1 = Addition::twoValues(x,y); printf("\nAddition Result: %d\n", z1); int z2 = Multiply::twoValues(x,y); printf("Multiply Result: %d\n", z2); delete corporation; return 0; }
File: src/Makefile
CXX = g++ CXXFLAGS = -g INCS = -I./ OBJS = Addition.o Multiply.o exampleapp: ExampleApp.cpp $(OBJS) $(CXX) $(CXXFLAGS) -o exampleapp ExampleApp.cpp $(OBJS) .cpp.o: $(CXX) $(CXXFLAGS) -c $< -o $@ $(INCS) clean: rm *.o exampleapp gmon.out
Compile: make
Run: exampleapp
- Program main() to specify tests to run: Main_TestAll.cpp
- Classes to perform the unit tests:
- Addition_Test.cpp
- Multiply_Test.cpp
The GoogleTest framework uses macros to define tests and apply tests:
GoogleTest support tests (TEST(class,test_name)) and test frameworks (TEST_F(class,test_name)). This tutorial will use the more extensive and complete test framework. The framework employs a user written test class derived from ::testing::Test which supplies SetUp() and TearDown() functions.
Tests:- ASSERT_XXX(): If assertion fails then processing of test terminate.
- EXPECT_XXX(): nonfatal failure, allowing processing to continue.
Test | Fatal | NonFatal |
---|---|---|
True | ASSERT_TRUE(condition) | EXPECT_TRUE(condition) |
False | ASSERT_FALSE(condition) | EXPECT_FALSE(condition) |
Equal | ASSERT_EQ(arg1,arg2) | EXPECT_EQ(arg1,arg2) |
Not Equal | ASSERT_NE(arg1,arg2) | EXPECT_NE(arg1,arg2) |
Less Than | ASSERT_LT(arg1,arg2) | EXPECT_LT(arg1,arg2) |
Less Than or Equal | ASSERT_LE(arg1,arg2) | EXPECT_LE(arg1,arg2) |
Greater Than | ASSERT_GT(arg1,arg2) | EXPECT_GT(arg1,arg2) |
Greater Than or Equal | ASSERT_GE(arg1,arg2) | EXPECT_GE(arg1,arg2) |
C String Equal | ASSERT_STREQ(str1,str2) | EXPECT_STREQ(str1,str2) |
C String Not Equal | ASSERT_STRNE(str1,str2) | EXPECT_STRNE(str1,str2) |
C String Case Equal | ASSERT_STRCASEEQ(str1,str2) | EXPECT_STRCASEEQ(str1,str2) |
C String Case Not Equal | ASSERT_STRCASENE(str1,str2) | EXPECT_STRCASENE(str1,str2) |
Verify that exception is thrown | ASSERT_THROW(statement,exception_type) | EXPECT_THROW(statement,exception_type) |
Verify that exception is thrown | ASSERT_ANY_THROW(statement) | EXPECT_ANY_THROW(statement) |
Verify that exception is NOT thrown | ASSERT_NO_THROW(statement) | EXPECT_NO_THROW(statement) |
Unit Test Code:
File: test/src/Main_TestAll.cpp#include <limits.h> #include "gtest/gtest.h" int main(int argc, char **argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); }
File: test/src/Addition_Test.cpp
#include <limits.h> #include "gtest/gtest.h" #include "Addition.h" class AdditionTest : public ::testing::Test { protected: virtual void SetUp() { } virtual void TearDown() { // Code here will be called immediately after each test // (right before the destructor). } }; TEST_F(AdditionTest,twoValues){ const int x = 4; const int y = 5; Addition addition; EXPECT_EQ(9,addition.twoValues(x,y)); EXPECT_EQ(5,addition.twoValues(2,3)); }
File: test/src/Multiply_Test.cpp
#include <limits.h> #include "gtest/gtest.h" #include "Multiply.h" class MultiplyTest : public ::testing::Test { protected: virtual void SetUp() { } virtual void TearDown() { } }; TEST_F(MultiplyTest,twoValues){ const int x = 4; const int y = 5; Multiply multiply; EXPECT_EQ(20,multiply.twoValues(x,y)); EXPECT_EQ(6,multiply.twoValues(2,3)); }
File: test/src/Makefile
CXX = g++ CXXFLAGS = -g -L/opt/gtest/lib -lgtest -lgtest_main -lpthread INCS = -I./ -I../../src -I/opt/gtest/include OBJS = ../../src/Addition.o Addition_Test.o ../../src/Multiply.o Multiply_Test.o testAll: $(OBJS) $(CXX) $(CXXFLAGS) $(INCS) -o testAll Main_TestAll.cpp $(OBJS) .cpp.o: $(CXX) $(CXXFLAGS) -c $< -o $@ $(INCS) clean: rm testAll *.o testAll.xml
Compile: make
Run: testAll --gtest_output="xml:./testAll.xml"
[==========] Running 2 tests from 2 test cases. [----------] Global test environment set-up. [----------] 1 test from MultiplyTest [ RUN ] MultiplyTest.twoValues [ OK ] MultiplyTest.twoValues (5 ms) [----------] 1 test from MultiplyTest (5 ms total) [----------] 1 test from AdditionTest [ RUN ] AdditionTest.twoValues [ OK ] AdditionTest.twoValues (4 ms) [----------] 1 test from AdditionTest (4 ms total) [----------] Global test environment tear-down [==========] 2 tests from 2 test cases ran. (9 ms total) [ PASSED ] 2 tests.
The following is the XML output from GoogleTest in JUnit format: testAll --gtest_output="xml:./testAll.xml"
Output File: test/src/testAll.xml
<?xml version="1.0" encoding="UTF-8"?> <testsuites tests="2" failures="0" disabled="0" errors="0" timestamp="2014-09-29T19:16:59" time="0" name="AllTests"> <testsuite name="MultiplyTest" tests="1" failures="0" disabled="0" errors="0" time="0"> <testcase name="twoValues" status="run" time="0" classname="MultiplyTest" /> </testsuite> <testsuite name="AdditionTest" tests="1" failures="0" disabled="0" errors="0" time="0"> <testcase name="twoValues" status="run" time="0" classname="AdditionTest" /> </testsuite> </testsuites>
Jenkins Ant file to compile and run a GoogleTest test:
File: ./build.xml<?xml version="1.0" encoding="UTF-8"?> <project name="CppApp" default="jenkins" basedir="."> <description> Jenkins Ant file for CppApp </description> <!-- set global properties for this build --> <property name="make.cmd" value="/usr/bin/make"/> <property name="build.native" value="./src"/> <property name="build.test" value="./test/src"/> <target name="init"> <!-- Create the time stamp --> <tstamp/> </target> <target name="compile" description="compile the source" > <exec dir="${build.native}" executable="${make.cmd}" failonerror="true"> </exec> </target> <arget name="compile-test" description="compile the source" depends="compile"> <exec dir="${build.test}" executable="${make.cmd}" failonerror="true"> </exec> </target> <target name="clean" description="clean up" > <exec dir="${build.native}" executable="${make.cmd}" failonerror="false"> <arg value="clean"/> </exec> <exec dir="${build.test}" executable="${make.cmd}" failonerror="false"> <arg value="clean"/> </exec> </target> <target name="run" description="Run application" depends="compile"> <exec dir="${build.native}" executable="exampleapp" failonerror="true"> </exec> </target> <target name="run-test" description="Run application" depends="compile-test"> <exec dir="${build.test}" executable="testAll" failonerror="true"> <arg line="--gtest_output="xml:./testAll.xml""/> </exec> </target> </project>
Building and running unit tests:
- Build: ant
- Test: ant run-test
GoogleTest presents its unit test output in JUnit format, a native format for Jenkins unit test reporting. The following is Jenkins configuration to plot JUnit results:
Jenkins Global Configuration:
Jenkins -> Manage Jenkins -> Configure System:Add the following for GoogleTest support:
- LD_LIBRARY_PATH: /opt/gtest/lib
- PATH: ./
[Potential Pitfall]: It the Jenkins PATH is not set to include the current working directory "./", you will get the error:
Execute failed: java.io.IOException: Cannot run program error=2, No such file or directory
The user paths should be set as well:
File: ~/.bashrc (snipet)
... ... if [ -d /opt/gtest/ ] then export GTEST_HOME=/opt/gtest export LD_LIBRARY_PATH=$GTEST_HOME/lib:$LD_LIBRARY_PATH fi ... ...
Jenkins Project Configuration:
Specify where the XML run results will be located.
Jenkins unit test reporting:
Note that there were two tests thus the count is two. Each test had multiple "EXPECT" statements but there were all part of the same test.
For more, see the YoLinux.com Tutorials Jenkins installation and configuration
- GoogleTest Home Page
- Apache Ant - build tool