This is a text-only version of the following page on https://raymii.org:
---
Title       : 	C++ project setup with CMake & unit tests (google test)
Author      : 	Remy van Elst
Date        : 	01-10-2019
Last update : 	06-11-2019
URL         : 	https://raymii.org/s/tutorials/Cpp_project_setup_with_cmake_and_unit_tests.html
Format      : 	Markdown/HTML
---



This guide will show you how to setup a new C++ project with CMake and unit tests
via Google's test framework. With this setup you can get started right away 
with test-driven-development in C++. It is also simple enough to look and figure
out how to add gtest to your existing project and start doing TDD on your legacy 
(existing) codebase. 

<p class="ad"> <b>Recently I removed all Google Ads from this site due to their invasive tracking, as well as Google Analytics. Please, if you found this content useful, consider a small donation using any of the options below:</b><br><br> <a href="https://leafnode.nl">I'm developing an open source monitoring app called  Leaf Node Monitoring, for windows, linux & android. Go check it out!</a><br><br> <a href="https://github.com/sponsors/RaymiiOrg/">Consider sponsoring me on Github. It means the world to me if you show your appreciation and you'll help pay the server costs.</a><br><br> <a href="https://www.digitalocean.com/?refcode=7435ae6b8212">You can also sponsor me by getting a Digital Ocean VPS. With this referral link you'll get $100 credit for 60 days. </a><br><br> </p>


The picture below shows the end result, a running unit test:

![a unit test][3]

There are a million different ways to "do" C++ projects, but using `CMake` and
the google testing framework has my preference. That's not to say that using a 
`Makefile` or Boost Unit Test is bad, use whatever suits your needs. This guide 
however will focus on just `CMake` and `gtest`.

It assumes a system running Ubuntu (18.04). It also works on Windows with `mingw`,
but I haven't tested in with MSVC. 

My preferred code editor is CLion from Jetbrains, which has most of this built 
in. This guide however focusses on the manual / command line way since CLion is
nonfree (and paid) software.

The process is not that complicated:

* Install software (cmake and googletest)
* Create folder structure
* Create the `CMakeLists.txt` files
* Create some sample code and sample tests
* Compile everything
* Run the tests

### Install cmake & googletest 

I assume you already have your compiler installed and working. Installing cmake
can be done with the package manager on Ubuntu:

    apt-get install cmake

On Windows, you can use MinGW or `cygwin` to install your development tools 
including CMake. Clion offers a nice GUI for that.

Googletest is available as a git repository which you can clone and then copy
into your project. You could go all fancy with CMake scripts to download it
if it's not already in your project, but since you can clone once and copy 
later on I choose not to automate it. Clone the repository:

    git clone https://github.com/google/googletest/

gtest comes with a `CMakeLists.txt` so integrating it in your project is easy.

### Folder structure

Create your C++ project folder. I like to keep the following structure for simple
projects:


    $ tree -L 2 ExampleProject/
    ExampleProject/
    |-- build/
    |-- CMakeLists.txt
    |-- lib/
    |   `-- googletest 
    |-- src/
    |   |-- CMakeLists.txt
    |   |-- Formula.cpp
    |   |-- Formula.h
    |   `-- main.cpp
    `-- tst/
        |-- CMakeLists.txt
        |-- Formula-test.cpp
        `-- main.cpp

Here is a oneliner to create the folders:

    mkdir -p ExampleProject/{build,lib,src,tst}

Copy the `googletest` repository folder your cloned earlier into the `lib/` folder.

If you have multiple components you can create extra sub folders, but that does
require tweaking the `CMakeLists.txt` files to work with multiple libraries. 

Most of my personal projects are simple enough to fit into one folder as above.

In the `tst` folder the unit tests reside. I try to keep the tests limited to 
the same function in seperate files. In the above example I have `Formula.h` and 
`Formula.cpp`, which house the example `Formula` class. All unit tests related
to this class thus should reside in `Formula-test.cpp`. 


### CMakeLists.txt

The file `CMakeLists.txt` contains a set of directives and instructions
describing the project's source files and targets (executable, library, or
both). This can get quite complex quite fast, CMake has many options. I try to
keep it simple in this guide.

I'm using a non-recommended way to include files. For simple projects with a 
few files you should use the following:

    add_executable(ExampleProject main.cpp file1.cpp file1.h)

I'm using this:

    file(GLOB_RECURSE SOURCES LIST_DIRECTORIES true *.h *.cpp)

That is a recursive search to include all `*.cpp` and `*.h` in the folder. In
my IDE I have [auto-reload][1] enabled, that way I can't forget to add a file
to `CMakeLists.txt` every time. For proper administration you should not use this
since it just includes everything, could have unwanted side-effects.

__Update 2019-11-07:__ If you want Boost in this setup, [read this article from me][4].


Each subdirectory in our case also needs a `CMakeLists.txt` file.

#### Main folder CMakeLists.txt

    cmake_minimum_required(VERSION 3.10)
    project(ExampleProject)

    set(CMAKE_CXX_STANDARD 14)

    include_directories(src)

    add_subdirectory(src)
    add_subdirectory(tst)
    add_subdirectory(lib/googletest)


The name of the project is `ExampleProject`, that variable is used in other files.
The rest of the file just includes the different subfolders. If you omit the 
`include_directories(src)`, your tests will not be able to find the header files.


#### src folder CMakeLists.txt:

    set(BINARY ${CMAKE_PROJECT_NAME})

    file(GLOB_RECURSE SOURCES LIST_DIRECTORIES true *.h *.cpp)

    set(SOURCES ${SOURCES})

    add_executable(${BINARY}_run ${SOURCES})

    add_library(${BINARY}_lib STATIC ${SOURCES})


The name of the compiled program will be `ExampleProject_run`, which is what
we defined in `add_executable`. The `add_library` is used to include the code
in the unit tests.

#### tst folder CMakeLists.txt:

    set(BINARY ${CMAKE_PROJECT_NAME}_tst)

    file(GLOB_RECURSE TEST_SOURCES LIST_DIRECTORIES false *.h *.cpp)

    set(SOURCES ${TEST_SOURCES})

    add_executable(${BINARY} ${TEST_SOURCES})

    add_test(NAME ${BINARY} COMMAND ${BINARY})

    target_link_libraries(${BINARY} PUBLIC ${CMAKE_PROJECT_NAME}_lib gtest)

This list used the `src` defined library and adds the tests as a target. The 
compiled executable file is named `ExampleProject_tst`. 



### Add some (example) source code and tests

At this point you start developing. But since this is a example setup, I'll add
a simple class file to show you how to do the unit tests.

#### Source Code

Copy the below code into your project:

`src/main.cpp`:

    #include <iostream>
    #include "Formula.h"

    int main() {
        std::cout << "Bla: " << Formula::bla(2) << std::endl;
        return 0;
    }


`src/Formula.h`: 

    #ifndef EXAMPLEPROJECT_FORMULA_H
    #define EXAMPLEPROJECT_FORMULA_H

    class Formula {
    public:
        static int bla(int arg1);
    };

    #endif //EXAMPLEPROJECT_FORMULA_H


`src/Formula.cpp`: 

    #include "Formula.h"

    int Formula::bla(int arg1) {
        return arg1 * 2;
    }


This function returns the given `int` multiplied by 2. 


#### Test code

The following code is to setup the unit tests. 

`tst/main.cpp`:

    #include "gtest/gtest.h"

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

This file will run all the tests and since we recursively included everything
with CMake, it will effectively run all tests in all files in this folder.

`tst/Formula-test.cpp`:

    #include "gtest/gtest.h"
    #include "Formula.h"

    TEST(blaTest, test1) {
        //arrange
        //act
        //assert
        EXPECT_EQ (Formula::bla (0),  0);
        EXPECT_EQ (Formula::bla (10), 20);
        EXPECT_EQ (Formula::bla (50), 100);
    }


The [Google Test Primer][2] is a great starting point to learn more on the specifics
of the testing framework.


### Compile all the things

Now that we have sourcecode and testcode in place we can compile everything 
(both the binary and the tests). 

Do note that you should do this in the `build` folder. If you do it in the 
main folder it will work, but it will litter up the directory.

    cd build
    cmake .. -DCMAKE_BUILD_TYPE=Debug -G "Unix Makefiles" 

Output: 

    -- The C compiler identification is GNU 7.4.0
    -- The CXX compiler identification is GNU 7.4.0
    -- Check for working C compiler: /usr/bin/cc
    -- Check for working C compiler: /usr/bin/cc -- works
    -- Detecting C compiler ABI info
    -- Detecting C compiler ABI info - done
    -- Detecting C compile features
    -- Detecting C compile features - done
    -- Check for working CXX compiler: /usr/bin/c++
    -- Check for working CXX compiler: /usr/bin/c++ -- works
    -- Detecting CXX compiler ABI info
    -- Detecting CXX compiler ABI info - done
    -- Detecting CXX compile features
    -- Detecting CXX compile features - done
    -- Found PythonInterp: /usr/bin/python (found version "2.7.15") 
    -- Looking for pthread.h
    -- Looking for pthread.h - found
    -- Looking for pthread_create
    -- Looking for pthread_create - not found
    -- Looking for pthread_create in pthreads
    -- Looking for pthread_create in pthreads - not found
    -- Looking for pthread_create in pthread
    -- Looking for pthread_create in pthread - found
    -- Found Threads: TRUE  
    -- Configuring done
    -- Generating done
    -- Build files have been written to: /home/remy/Repo/ExampleProject/build


There are now a bunch of files and folders in the `build` folder, most important,
the `Makefile`. You can now compile the project:


    make all

Output:

    Scanning dependencies of target ExampleProject_run
    [  8%] Building CXX object src/CMakeFiles/ExampleProject_run.dir/Formula.cpp.o
    [ 16%] Building CXX object src/CMakeFiles/ExampleProject_run.dir/main.cpp.o
    [ 25%] Linking CXX executable ExampleProject_run
    [ 25%] Built target ExampleProject_run
    Scanning dependencies of target ExampleProject_lib
    [ 33%] Building CXX object src/CMakeFiles/ExampleProject_lib.dir/Formula.cpp.o
    [ 41%] Building CXX object src/CMakeFiles/ExampleProject_lib.dir/main.cpp.o
    [ 50%] Linking CXX static library libExampleProject_lib.a
    [ 50%] Built target ExampleProject_lib
    Scanning dependencies of target gtest
    [ 58%] Building CXX object lib/googletest/CMakeFiles/gtest.dir/src/gtest-all.cc.o
    [ 66%] Linking CXX static library ../libgtestd.a
    [ 66%] Built target gtest
    Scanning dependencies of target ExampleProject_tst
    [ 75%] Building CXX object tst/CMakeFiles/ExampleProject_tst.dir/Formula-test.cpp.o
    [ 83%] Linking CXX executable ExampleProject_tst
    [ 83%] Built target ExampleProject_tst
    Scanning dependencies of target gtest_main
    [ 91%] Building CXX object lib/googletest/CMakeFiles/gtest_main.dir/src/gtest_main.cc.o
    [100%] Linking CXX static library ../libgtest_maind.a
    [100%] Built target gtest_main


You now have two executable files, as defined in the `CMakeLists.txt`:

    $ find . -executable -type f
    ./tst/ExampleProject_tst
    ./src/ExampleProject_run

### Run all the things

If all went well, the code should run:

    ./src/ExampleProject_run

Output:

    Bla: 4

The tests as well:

    ./tst/ExampleProject_tst 

Output:

    [==========] Running 1 test from 1 test suite.
    [----------] Global test environment set-up.
    [----------] 1 test from blaTest
    [ RUN      ] blaTest.test1
    [       OK ] blaTest.test1 (0 ms)
    [----------] 1 test from blaTest (0 ms total)

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


A quick one-liner to compile and run the tests. You can run this whenever
you want to re-run the tests (after changing code for example):

    make ExampleProject_tst; tst/ExampleProject_tst 

Output:

    [ 37%] Built target ExampleProject_lib
    [ 62%] Built target gtest
    Scanning dependencies of target ExampleProject_tst
    [ 75%] Building CXX object tst/CMakeFiles/ExampleProject_tst.dir/Formula-test.cpp.o
    [ 87%] Linking CXX executable ExampleProject_tst
    [100%] Built target ExampleProject_tst
    [==========] Running 1 test from 1 test suite.
    [----------] Global test environment set-up.
    [----------] 1 test from blaTest
    [ RUN      ] blaTest.test1
    /home/remy/Repo/ExampleProject/tst/Formula-test.cpp:8: Failure
    Expected equality of these values:
      Formula::bla (1)
        Which is: 2
      0
    [  FAILED  ] blaTest.test1 (0 ms)
    [----------] 1 test from blaTest (0 ms total)

    [----------] Global test environment tear-down
    [==========] 1 test from 1 test suite ran. (0 ms total)
    [  PASSED  ] 0 tests.
    [  FAILED  ] 1 test, listed below:
    [  FAILED  ] blaTest.test1

     1 FAILED TEST

As you can see I changed a unit test so that it failed. 


[1]: https://www.jetbrains.com/help/clion/reloading-project.html
[2]: https://github.com/google/googletest/blob/master/googletest/docs/primer.md
[3]: /s/inc/img/unit-test.png
[4]: /s/snippets/std_string_to_lowercase_or_uppercase_in_cpp.html#toc_4

---

License:
All the text on this website is free as in freedom unless stated otherwise. 
This means you can use it in any way you want, you can copy it, change it 
the way you like and republish it, as long as you release the (modified) 
content under the same license to give others the same freedoms you've got 
and place my name and a link to this site with the article as source.

This site uses Google Analytics for statistics and Google Adwords for 
advertisements. You are tracked and Google knows everything about you. 
Use an adblocker like ublock-origin if you don't want it.

All the code on this website is licensed under the GNU GPL v3 license 
unless already licensed under a license which does not allows this form 
of licensing or if another license is stated on that page / in that software:

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

Just to be clear, the information on this website is for meant for educational 
purposes and you use it at your own risk. I do not take responsibility if you 
screw something up. Use common sense, do not 'rm -rf /' as root for example. 
If you have any questions then do not hesitate to contact me.

See https://raymii.org/s/static/About.html for details.