Install git and compile
$ git clone https://github.com/google/googletest.git -b release-1.12.0
$ cd googletest
$ mkdir build
$ cd build
$ cmake .. -DBUILD_GMOCK=OFF
$ makePlease check the current release version and update the git clone command accordingly.
After this you releated static binaries will be in the googletest/build/lib/ location.
To test it lets write simpe hello world gtest application. (you can find it as an example.cpp in the repo)
#include <iostream>
#include <gtest/gtest.h>
using namespace std;
TEST(TestName, test1)
{
ASSERT_EQ(1, 1);
}
int main(int argc, char **argv)
{
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}What we have here, one main method and one test method has name TestName.test1. To compile it you can use simply:
// g++ example.cpp googletest/build/lib/libgtest.a googletest/build/lib/libgtest_main.a -lpthread -I googletest/googletest/include/After this it will give you a.out when you run it.
jnano@jnano:~$ ./a.out
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from TestName
[ RUN ] TestName.test1
[ OK ] TestName.test1 (0 ms)
[----------] 1 test from TestName (0 ms total)
[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (0 ms total)
[ PASSED ] 1 test.But we do not use commandline to compile our projects. Then it is a good idea to use cmake to compile all.
cmake_minimum_required(VERSION 3.10)
set(CMAKE_CXX_STANDARD 14)
project(ExampleGtest LANGUAGES CXX)
include_directories(googletest/googletest/include googletest/googlemock/include include)
add_executable(${PROJECT_NAME} src/example.cpp)
target_link_libraries(${PROJECT_NAME} ${CMAKE_SOURCE_DIR}/googletest/build/lib/libgtest.a ${CMAKE_SOURCE_DIR}/googletest/build/lib/libgtest_main.a ${CMAKE_SOURCE_DIR}/googletest/build/lib/libgmock.a ${CMAKE_SOURCE_DIR}/googletest/build/lib/libgmock_main.a)
set(CMAKE_THREAD_LIBS_INIT "-lpthread")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread")
set(CMAKE_HAVE_THREADS_LIBRARY 1)
set(CMAKE_USE_WIN32_THREADS_INIT 0)
set(CMAKE_USE_PTHREADS_INIT 1)
set(THREADS_PREFER_PTHREAD_FLAG ON)What do we apply here, simply link the static binaries and include headers with cmake. But this is a best practice ? Nope. Then what ?
If you check here https://cmake.org/cmake/help/latest/module/FetchContent.html then you are able to see that we can fetch the content form the repositroy directly. No need to build. Like :
cmake_minimum_required(VERSION 3.14)
set(CMAKE_CXX_STANDARD 14)
project(ExampleGtest LANGUAGES CXX)
include(FetchContent)
FetchContent_Declare(
googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG bf66935e07825318ae519675d73d0f3e313b3ec6
)
FetchContent_MakeAvailable(googletest)
add_executable(${PROJECT_NAME} src/example.cpp)
target_link_libraries(${PROJECT_NAME} gtest_main gmock_main)
include(GoogleTest)
gtest_discover_tests(${PROJECT_NAME})
#include headers
include_directories(include)This will compile and fectch the gtest accordingly, so no need to compile by hand. And no need to care about the static files. You can give the desired commit number. Becareful about cmake version, it can be not available for old versions. And there is one more benefit here. gtest_discover_tests make us to remove main() function in the example.cpp. That means we do not have to init google test and run it. Just simply define the tests.
If you check the cmakelists.txt in the repository then you will see extra commands like gcov and lcov. These are used to calculate line coverage. What is linecoverage ? Simply it will give you the numbers of the lines and coverage what you test in the your code base.
# Create the gcov target. Run coverage tests with 'make gcov'
add_custom_target(gcov
COMMAND mkdir -p gcoverage
COMMAND ${CMAKE_MAKE_PROGRAM} test
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
)
add_custom_command(TARGET gcov
COMMAND echo "=================== GCOV ===================="
COMMAND ${GCOV_PATH} -b ${CMAKE_SOURCE_DIR}/src/*.cpp -o ${OBJECT_DIR}
COMMAND echo "-- Source diretorie: ${CMAKE_SOURCE_DIR}/src/"
COMMAND echo "-- Coverage files have been output to ${CMAKE_BINARY_DIR}/gcoverage"
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/gcoverage
)
add_dependencies(gcov ${PROJECT_NAME})
add_custom_target(lcov
COMMAND mkdir -p lcoverage
)
add_custom_command(TARGET lcov
COMMAND echo "=================== LCOV ===================="
COMMAND echo "-- Passing lcov tool under code coverage"
COMMAND lcov --capture --directory ${OBJECT_DIR} --output-file lcoverage/main_coverage.info -b .
COMMAND echo "-- Remove undesired files from main_coverage"
COMMAND lcov --remove lcoverage/main_coverage.info '*/usr/include/*' '*/gtest/*' -o lcoverage/filtered_coverage.info
COMMAND echo "-- Generating HTML output files"
COMMAND genhtml lcoverage/filtered_coverage.info --output-directory lcoverage
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
)
# Make sure to clean up the coverage folder
set_property(DIRECTORY APPEND PROPERTY ADDITIONAL_MAKE_CLEAN_FILES gcoverage)
# Create the gcov-clean target. This cleans the build as well as generated
# .gcda and .gcno files. ${CMAKE_MAKE_PROGRAM} is /usr/bin/make
add_custom_target(refresh
COMMAND ${CMAKE_MAKE_PROGRAM} clean
COMMAND rm CMakeCache.txt
COMMAND rm -f ${OBJECT_DIR}/*.gcno
COMMAND rm -f ${OBJECT_DIR}/*.gcda
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
)I created here three differrent more targets in the cmakelists.txt. What is target ? when you run "make" command you will be able to "make -targetName-". For instance one target is "gcov". After compilation, when you run "make gcov" you will see that all files are processed in your application. This will produce .gcov files where you can find line coverage information. But it is not human readable and there are other files that we do not need. It will produce for all linked binaries for instance gtest and included libraries. We should remove them. We can remove them during lcov process. Lcov is a tool to create html readable format for line coverage. then run "make lcov"
Generating output.
Processing file src/foo.cpp
Processing file src/example.cpp
Processing file src/dummy.cpp
Writing directory view page.
Overall coverage rate:
lines......: 52.6% (10 of 19 lines)
functions..: 70.0% (7 of 10 functions)
Built target lcovThis will produce lcoverage directory go there, then you may be able to see index.html file. Open it using one of your favorite web browser. You should see sth like this:
You can check the line coverage visually file by file. Btw, I added all the stuff in one CMakeLists.txt file, I think it is not best practice, it is a good idea to split it :)
So what is gmock ? When you have dependecies for your object, during the unit testing these dependencies should be mocked. When you mocked them, your object does not call the real methods of dependencies but mocked methods. It will be good to have to use interfaces in your design. ( interface is good thing )
For instance in this example you can see that foo class is mocked, if you check under include directory, there is a mock_foo.hpp file. ( keeping this in include with others is not a good idea ofcourse).
#include "foo_if.hpp"
#include <gmock/gmock.h>
#include <string>
class mockFoo : public fooIf
{
public:
MOCK_METHOD(void, fooStr, (std::string& str), (override));
MOCK_METHOD(void, fooThrow, (), (override));
MOCK_METHOD(void, callbackMethod, (std::function<void(void)>),(override));
};As you see, it implements the fooIf interface. The way of implementaion is different from syntax perspective. You need to use MOCK_METHOD gtest macro and first define return of function then name finally the arguments. To inject this, you will use constructor of your object.
testDummy = std::make_unique<dummy>(fooMock);Because we can pass it, why ? we use same interface, so we are able to see here that one of the advantage of using interfaces.
Then in the test fixture, you can use it like :
EXPECT_CALL(fooMock, fooStr(::testing::_)).WillOnce(::testing::SetArgReferee<0>(returnStr));What does it mean ? Simply fooStr method of the mock, must be called and return the string from argument for this test, otherwise gtest will fail.
If you want you can check this diagram to understand the relationship better for mock and test classess. ( design is for this simple example. Relationship can be different according to you requirements. )
so as you see test class (text fixture) has composition dependecy to dummy class which will be tested, and to mockFoo class not foo class. In this way, during test, real methods of the foo object will not be called. This is because if the mock mechanism.
So the question is that why we do not use foo itself instead of mockFoo. Because we want to test only dummy class not foo or others. This is the point of unit test. Foo object can have some dependecny which can not be relevant for dummy object test. We need to remove them from the test but at the same time we should not change anything in the dummy implementation so we should mock the dependecies of the dummy object which we want to test.
