Tutorial
Once integrated in your project, the Pytest::Pytest
target and the pytest_discover_tests()
function are available for using.
Using the target
Let’s consider a project which wraps C++ logic with Python bindings. We need to
add a CMakeLists.txt
configuration file to add Python tests within the
same directory. The Pytest command can be easily implemented using the
add_test function:
add_test(
NAME PythonTest
COMMAND Pytest::Pytest ${CMAKE_CURRENT_SOURCE_DIR}
)
For the tests to run, the PYTHONPATH
environment variable must be
updated to locate the built package library. We can use an expression generator
to resolve the path of the dependent target directory dynamically:
set_tests_properties(
TEST PythonTest
PROPERTY ENVIRONMENT
PYTHONPATH=$<TARGET_FILE_DIR:MyLibrary>:$ENV{PYTHONPATH}
)
The shared library might also be required during runtime execution, so its location should be provided:
set_tests_properties(
TEST PythonTest
APPEND PROPERTY ENVIRONMENT
LD_LIBRARY_PATH=$<TARGET_FILE_DIR:MyLibrary>:$ENV{LD_LIBRARY_PATH}
)
Warning
The environment variable used to locate shared libraries depends on the
platform. LD_LIBRARY_PATH
is used on Linux,
DYLD_LIBRARY_PATH
on macOS, and PATH
on Windows.
After building the project, the command can then be executed by CTest. If all tests are successful, the output will look as follows:
Start 1: PythonTest
1/1 Test #1: PythonTest ....................... Passed 0.55 sec
However, if only one test is unsuccessful, the entire test suite will be marked as failed.
Start 1: PythonTest
1/1 Test #1: PythonTest .......................***Failed 0.47 sec
Using the function
A pytest_discover_tests()
function is provided to create CTest
tests for each Python test collected. Therefore, the configuration added in the
previous section could be replaced by the following:
pytest_discover_tests(
PythonTest
LIBRARY_PATH_PREPEND
$<TARGET_FILE_DIR:MyLibrary>
PYTHON_PATH_PREPEND
$<TARGET_FILE_DIR:MyLibrary>
TRIM_FROM_NAME "^test_"
DEPENDS MyLibrary
)
This will create a new PythonTest target, dependent on the MyLibrary target.
The expected environment can be defined simply with the LIBRARY_PATH_PREPEND
and PYTHON_PATH_PREPEND
arguments, which both accept multiple values. The
environment variable used to locate shared libraries will be automatically
chosen according to the platform.
Pytest usually requires tests to start with a
specific prefix,
which can be trimmed using the TRIM_FROM_NAME
argument. The value can use a
regular expression to
match the part of the test name that should be trimmed.
A list of dependent targets can be defined with the DEPENDS
argument, which
accepts multiple values.
After building the project, running CTest will display the tests as follows:
Start 1: PythonTest.greet_world
1/4 Test #1: PythonTest.greet_world ........... Passed 0.47 sec
Start 2: PythonTest.greet_john
2/4 Test #2: PythonTest.greet_john ............ Passed 0.47 sec
Start 3: PythonTest.greet_julia
3/4 Test #3: PythonTest.greet_julia ........... Passed 0.47 sec
Start 4: PythonTest.greet_michael
4/4 Test #4: PythonTest.greet_michael ......... Passed 0.54 sec
It is also possible to regroup all tests under one CTest test, as was the case when using the target. This can be useful during development to ensure that the tests run faster, especially if you use fixtures with a broader scope.
This can be done by setting the BUNDLE_TESTS
argument to True:
pytest_discover_tests(
PythonTest
LIBRARY_PATH_PREPEND
$<TARGET_FILE_DIR:MyLibrary>
PYTHON_PATH_PREPEND
$<TARGET_FILE_DIR:MyLibrary>
TRIM_FROM_NAME "^test_"
DEPENDS MyLibrary
BUNDLE_TESTS True
)
After re-building the project, running CTest will display the tests as follows:
Start 1: PythonTest
1/1 Test #1: PythonTest ....................... Passed 0.51 sec
Note
The BUNDLE_PYTHON_TESTS
environment variable can also set this
argument dynamically.