1. Overview

GoogleTest is a popular unit test framework for C++ applications. It’s based on the xUnit architecture, which has several components such as test runner, test case, test suite, etc.

When GoogleTest is built from the source code, static libraries are generated instead of shared libraries by default. However, we may prefer working with shared libraries.

In this tutorial, we’ll discuss how to set up GoogleTest as a shared library in Linux. Firstly, we’ll download GoogleTest. Then, we’ll see how to build it and generate the shared libraries. Finally, we’ll work on a unit test example that uses the generated shared libraries.

2. Downloading GoogleTest

We can download GoogleTest from GitHub using the git command:

$ pwd
/home/centos/work
$ git clone https://github.com/google/googletest.git -b v1.13.0
Cloning into 'googletest'...

Cloning GoogleTest starts after running git clone https://github.com/google/googletest.git -b v1.13.0. We specify the version using the -b option of git clone. When cloning finishes, we have the googletest directory in the current directory:

$ ls -l
total 4
drwxrwxr-x 8 centos centos 4096 Aug  2 11:55 googletest

The googletest directory contains the downloaded source code.

Another alternative to downloading GoogleTest using git is to download the source code manually from the website https://github.com/google/googletest/releases. This website contains the releases of GoogleTest.

We can download either the tar.gz archive or the zip file of the source code. Once we download the source code, for example, the tar.gz archive corresponding to v1.13.0 branch, we can extract it using the gtar command:

$ gtar zxvf googletest-1.13.0.tar.gz > /dev/null
$ ls -l
total 1912
drwxrwxr-x 7 centos centos    4096 Jan 17  2023 googletest-1.13.0
-r-------- 1 centos centos  862871 Jul 23 15:59 googletest-1.13.0.tar.gz

We can also use the tar command instead of gtar. We direct the output of gtar to /dev/null just to avoid the long list of the files in the archive during extraction. The files in the archive are extracted into the directory googletest-1.13.0 as the output of ls shows.

3. Building GoogleTest

We’ll use the googletest directory cloned using git:

$ cd googletest
$ mkdir build
$ cd build

We create the build directory to hold the build output.

Next, we generate build scripts and a Makefile using cmake:

$ cmake .. -DBUILD_SHARED_LIBS=ON -DCMAKE_INSTALL_PREFIX=/home/centos/googletest
-- The C compiler identification is GNU 11.2.1
-- The CXX compiler identification is GNU 11.2.1
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info – done
-- Check for working C compiler: /usr/bin/cc – skipped
-- Detecting C compile features
-- Detecting C compile features – done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info – done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features – done
-- Found Python: /usr/bin/python3.9 (found version "3.9.9") found components: Interpreter 
-- Looking for pthread.h
-- Looking for pthread.h – found
-- Performing Test CMAKE_HAVE_LIBC_PTHREAD
-- Performing Test CMAKE_HAVE_LIBC_PTHREAD – Success
-- Found Threads: TRUE  
-- Configuring done
-- Generating done
-- Build files have been written to: /home/centos/work/googletest/build

The -DBUILD_SHARED_LIBS=ON parameter builds GoogleTest libraries as shared libraries. Otherwise, the libraries are built as static libraries.

The -DCMAKE_INSTALL_PREFIX parameter specifies the directory into which the built libraries and header files are installed when we run make install. It’s /home/centos/googletest in our case.

Now, we can build GoogleTest using make:

$ make
[ 12%] Building CXX object googletest/CMakeFiles/gtest.dir/src/gtest-all.cc.o
[ 25%] Linking CXX shared library ../lib/libgtest.so
[ 25%] Built target gtest
[ 37%] Building CXX object googlemock/CMakeFiles/gmock.dir/src/gmock-all.cc.o
[ 50%] Linking CXX shared library ../lib/libgmock.so
[ 50%] Built target gmock
[ 62%] Building CXX object googlemock/CMakeFiles/gmock_main.dir/src/gmock_main.cc.o
[ 75%] Linking CXX shared library ../lib/libgmock_main.so
[ 75%] Built target gmock_main
[ 87%] Building CXX object googletest/CMakeFiles/gtest_main.dir/src/gtest_main.cc.o
[100%] Linking CXX shared library ../lib/libgtest_main.so
[100%] Built target gtest_main

The built libraries and header files are in the current directory. Now, let’s install them to the directory we specify using -DCMAKE_INSTALL_PREFIX parameter, /home/centos/googletest:

$ make install > /dev/null

We direct the output to /dev/null as the output of make install is long. Let’s check the directories created in /home/centos/googletest:

$ ls -l /home/centos/googletest
total 4
drwxr-xr-x 4 centos centos   32 Aug  2 15:41 include
drwxrwxr-x 4 centos centos 4096 Aug  2 15:41 lib64
$ ls -l /home/centos/googletest/lib64/
total 2144
drwxrwxr-x 3 centos centos      19 Aug  2 15:41 cmake
lrwxrwxrwx 1 centos centos      23 Aug  2 15:41 libgmock_main.so -> libgmock_main.so.1.13.0
-rwxr-xr-x 1 centos centos   17832 Aug  2 15:39 libgmock_main.so.1.13.0
lrwxrwxrwx 1 centos centos      18 Aug  2 15:41 libgmock.so -> libgmock.so.1.13.0
-rwxr-xr-x 1 centos centos  534344 Aug  2 15:39 libgmock.so.1.13.0
lrwxrwxrwx 1 centos centos      23 Aug  2 15:41 libgtest_main.so -> libgtest_main.so.1.13.0
-rwxr-xr-x 1 centos centos   17504 Aug  2 15:39 libgtest_main.so.1.13.0
lrwxrwxrwx 1 centos centos      18 Aug  2 15:41 libgtest.so -> libgtest.so.1.13.0
-rwxr-xr-x 1 centos centos 1616664 Aug  2 15:39 libgtest.so.1.13.0
drwxrwxr-x 2 centos centos      80 Aug  2 15:41 pkgconfig

The installation is complete now. The header files are in the /home/centos/googletest/include directory, whereas the shared libraries are in the /home/centos/googletest/lib64 directory.

4. Testing the Build

We’ll test the installation of GoogleTest in this section.

4.1. The Class to Test

We’ll test the C++ class, Counter, whose declaration is in the header file, Counter.h:

#ifndef _COUNTER_H_
#define _COUNTER_H
class Counter
{
    private:
        int counter_value {0};
        int max_counter_value;
        
    public:
        Counter(int);

        void increment();

        void decrement();

        int getCounter() const;
};

#endif

The source code of the Counter class is in Counter.cc:

#include "Counter.h"

Counter::Counter(int max_value)
:max_counter_value{max_value}
{}

void Counter::increment(){
    if (counter_value < max_counter_value)
        counter_value++;
}

void Counter::decrement(){
    if (counter_value > 0)
        counter_value--;
}

int Counter::getCounter() const {
    return counter_value;
}

The Counter class has two member variables. The first one, counter_value, is the value of the counter. Its default value is 0. The other member variable, max_counter_value, defines the maximum value that counter_value can take. We supply the value of max_counter_value as a parameter to the constructor.

The increment() member function of Counter increases the value of counter_value. However, it doesn’t let it be bigger than max_counter_value.

The decrement() member function of Counter decreases the value of counter_value. Similarly, it doesn’t let it be less than 0.

The getCounter() member function of Counter returns the value of counter_value.

4.2. Unit Test Code

We’ll test the Counter class using the unit test code in Counter_unittest.cc:

#include "Counter.h"
#include "gtest/gtest.h"

TEST(Counter, increment) {
    // Check the initial counter value
    Counter counter(2);
    EXPECT_EQ(0, counter.getCounter());

    // Increment counter and check its value
    counter.increment();
    EXPECT_EQ(1, counter.getCounter());

    // Increment counter and check its value
    counter.increment();
    EXPECT_EQ(2, counter.getCounter());

    // Check if counter can get a value bigger than the value given during construction
    counter.increment();
    EXPECT_EQ(2, counter.getCounter());
}

TEST(Counter, decrement) {
    // Check the initial counter value
    Counter counter(2);
    EXPECT_EQ(0, counter.getCounter());

    // Check if counter can get a value less than zero
    counter.decrement();
    EXPECT_EQ(0, counter.getCounter());

    // Increment counter twice and check its value
    counter.increment();
    counter.increment();
    EXPECT_EQ(2, counter.getCounter());

    // Decrement counter and check its value
    counter.decrement();
    EXPECT_EQ(1, counter.getCounter());

    // Decrement counter and check its value
    counter.decrement();
    EXPECT_EQ(0, counter.getCounter());
}

int main(int argc, char **argv) {
    ::testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}

The test suite has two tests. Let’s discuss the first test, starting with the statement TEST(Counter, increment), briefly. This test tests the increment() member function of Counter.

We must include the header file, Counter.h, to use the Counter class. Similarly, we must include the header file, gtest.h, to use GoogleTest.

The statement Counter counter(2) creates an object of type Counter. The name of the object is counter.

After creating the object, we check the initial value of counter using the statement EXPECT_EQ(0, counter.getCounter()). The EXPECT_EQ() macro checks the equality of its parameters. If they aren’t equal, the assertion fails. The initial value of counter must be 0.

Then, we increment the value of counter using counter.increment() twice. The expected values are 1 and 2.

Finally, we increment the value of counter once more and check if the value of counter can exceed the value given during construction. The expected value is 2.

The second test tests the functionality of the decrement() member function of Counter. The test logic is the same.

We run the tests using the RUN_ALL_TESTS() macro in main(). We must call the InitGoogleTest() function in the testing namespace before calling RUN_ALL_TESTS(). It’s important to return the value of RUN_ALL_TESTS() as the return value of main().

4.3. Building the Counter Class

The header and source files for the Counter class are in the directory /home/centos/work/Counter. We’ll build the Counter class as a shared library:

$ ls
Counter.cc Counter.h
$ g++ -shared -o libcounter.so Counter.cc
$ ls
Counter.cc Counter.h libcounter.so

We use the -shared option of g++ for building the shared library. The -o option specifies the name of the generated shared library, which is libcounter.so.

4.4. Building and Running the Unit Test

We’ll build the unit test, Counter_unittest.cc, using g++:

$ g++ -o Counter_unittest Counter_unittest.cc -I/home/centos/googletest/include/ -I/home/centos/work/Counter/ -L/home/centos/googletest/lib64/ -L/home/centos/work/Counter/ -lgtest -lcounter

We specify the name of the test executable as Counter_unittest using the -o option of g++.

The -I/home/centos/googletest/include -I/home/centos/work/Counter/ part of the command specifies the paths of the included header files in Counter_unittest.cc, namely gtest.h and Counter.h. Similarly, the -L/home/centos/googletest/lib64/ -L/home/centos/work/Counter/ part specifies the paths of the shared libraries that the linker must link with the executable. These libraries are libcounter.so and libgtest.so, which we specify using -lgtest -lcounter.

Having built the test executable, we’ll run the unit test. However, we must first add the path of the shared libraries to the LD_LIBRARY_PATH environment variable for finding the shared libraries during runtime:

$ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/centos/work/Counter:/home/centos/googletest/lib64

Otherwise, the linker cannot find the shared libraries and gives an error.

Finally, it’s time to run the unit test:

$ ./Counter_unittest
[==========] Running 2 tests from 1 test suite.
[----------] Global test environment set-up.
[----------] 2 tests from Counter
[ RUN      ] Counter.increment
[       OK ] Counter.increment (0 ms)
[ RUN      ] Counter.decrement
[       OK ] Counter.decrement (0 ms)
[----------] 2 tests from Counter (0 ms total)

[----------] Global test environment tear-down
[==========] 2 tests from 1 test suite ran. (0 ms total)
[  PASSED  ] 2 tests.

As the output of the tests shows, both tests, namely Counter.increment and Counter.decrement, were run by the GoogleTest framework. They pass successfully.

Therefore, we’re successful in using GoogleTest shared libraries in a unit test case.

5. Conclusion

In this article, we discussed how to set up GoogleTest as a shared library in Linux. Firstly, we downloaded GoogleTest. Then, we built the libraries and saw that we must use the -DBUILD_SHARED_LIBS=ON parameter to build the shared libraries. Finally, we looked at a unit test and ran it using the GoogleTest shared libraries.

Comments are closed on this article!