Running Python unit tests with CMake


Welcome to the next pikoTutorial !

If you haven’t read the previous pikoTutorial on setting up a Python project with CMake, you may want to check it out first because in today’s article I’ll be using the setup we’ve done there:

project/
├── app1/
    ├── CMakeLists.txt
    ├── main.py
    ├── requirements.txt
├── app2/
    ├── CMakeLists.txt
    ├── main.py
    ├── requirements.txt
├── app3/
    ├── CMakeLists.txt
    ├── main.cpp
├── build/
├── CMakeLists.txt

However, I will extend it with the test directories for each Python application:

project/
├── app1/
    ├── CMakeLists.txt
    ├── main.py
    ├── requirements.txt
    ├── test
        ├── test_app.py
├── app2/
    ├── CMakeLists.txt
    ├── main.py
    ├── requirements.txt
    ├── test
        ├── test_app.py
├── app3/
    ├── CMakeLists.txt
    ├── main.cpp
├── build/
├── CMakeLists.txt

Adding test targets

As I was showing in the pikoTutorial about running C++ tests with CMake, to enable running unit tests with ctest, we must extend app1/CMakeLists.txt file with add_test call:

CMake
# specify app1 virtual environment directory
set(APP1_VENV ${CMAKE_BINARY_DIR}/app1_venv)
# create virtual environment for app1
create_venv(${APP1_VENV} ${CMAKE_SOURCE_DIR}/app1/requirements.txt)
# add custom target to run app1
add_custom_target(run_app1
    COMMAND ${APP1_VENV}/bin/python ${CMAKE_SOURCE_DIR}/app1/main.py
    DEPENDS ${APP1_VENV}
)
# add unit tests
add_test(
    NAME app1_unit_test
    COMMAND ${APP1_VENV}/bin/python -m unittest discover ${CMAKE_SOURCE_DIR}/app1/test
)

The same goes for app2/CMakeLists.txt file. To make it work, we also need to add enable_testing() to the main CMakeLists.txt file.

Now I can run unit tests for both Python applications just by calling:

Bash
ctest

What will produce the following output:

    Start 1: app1_unit_test
1/2 Test #1: app1_unit_test ...................   Passed    0.07 sec
    Start 2: app2_unit_test
2/2 Test #2: app2_unit_test ...................   Passed    0.06 sec

Mixing unit tests between the languages

It’s time extend the configuration – let’s add tests not only for Python applications, but also for app3 which is a C++ application:

├── app3/
    ├── CMakeLists.txt
    ├── main.cpp
    ├── test
        ├── test_app.cpp

I will add the simplest C++ unit test definition to app3/CMakeLists.txt file:

CMake
# create an executable out of C++ code
add_executable(main main.cpp)
# add custom target to run app3
add_custom_target(run_app3
    COMMAND ${CMAKE_BINARY_DIR}/app3/main
    DEPENDS main
)
# add unit tests executable
add_executable(app3_unit_test test/test_app.cpp)
# add unit tests
add_test(
    NAME app3_unit_test
    COMMAND app3_unit_test
)

Now I call ctest and voilà – both Python and C++ tests executed with a single command:

    Start 1: app1_unit_test
1/3 Test #1: app1_unit_test ...................   Passed    0.06 sec
    Start 2: app2_unit_test
2/3 Test #2: app2_unit_test ...................   Passed    0.06 sec
    Start 3: app3_unit_test
3/3 Test #3: app3_unit_test ...................   Passed    0.00 sec