Catch2 is a C++ unit test framework. It also provides some microbenchmarking features and some rudimentary Behaviour Driven Development functionality.
Version 2 of Catch2 supports C++11 and newer. For pre C++111 compilers it's possible to use the old Catch version 1 branch. But don't expect heavy development on this old branch.
Catch2 is like most other C++ testing frameworks based on macros. C++ still
can't get rid of the old c macros because of the lack of replacements like
__FILE__
or __LINE__
and others. But as soon as macros are involved you have to
be careful. Several limitations are in place like missing debugging abilities,
unexepected side effects and they clutter the global namespace.
For instance you'll have to take care if you want to use expressions with commas
in a assertion macro: you can use a typedef or parenthise the expression.
Otherwise the assertion macro will recognise the commas as parameter delimiters
and fail to process the macro replacement.2
Consider also the recommendations for using macros.
The recommended way to use catch2 is to use its CMake integration.
For a simple start if you only want to do some prototyping download the single header catch.hpp and copy it into your prototyping project.3
To get started create two cpp-files: lets call them main.cpp and
test_catch2.cpp and open them with your favourite vim editor:
$ touch {main,test_catch2}.cpp
$ vim !$ # designates the last argument of the preceding command. This is the shortened version of !!:$ (from bash reference manual)
// file main.cpp
#define CATCH_CONFIG_MAIN
#include "lib/catch.hpp" # or wherever you put the catch header file
// file test_catch2.cpp
#include "lib/catch.hpp" # or wherever you put the catch header file
// As a warm up a very simple test with the require assertion macro
TEST_CASE() {
REQUIRE(2 * 21 == 42);
}
Firstly there is the TEST_CASE macro which defines a test case. A test case can include several sections. These test cases and the sections behave a bit different than the usual xUnit fixture behaviour. For each section with a test case the test case code is executed from the start. Sections can be nested and every leaf section will be run exactly one time.
Several assertion macros are available:
REQUIRE(expression);
aborts when the test failsCHECK(expression);
cotinues when the test failsREQUIRE_FALSE(expression);
negation of the aboveCHECK_FALSE(expression);
negation of the aboveREQUIRE_THROWS(expression);
expects an exception to be thrownCHECK_THROWS(expression);
expects an exception to be thrownREQUIRE_NOTHROW(expression);
expects no exception to be thrownCHECK_NOTHROW(expression);
expects no exception to be thrownREQUIRE_THROWS_AS(expression, exception);
expects a specific exceptionCHECK_THROWS_AS(expression, exception);
expects a specific exceptionLets have a more complicated example:
TEST_CASE("First test case") {
std::string testString("This is a test string containing some characters");
REQUIRE_FALSE(testString.empty());
SECTION("Clear the string") {
testString.clear();
REQUIRE(testString.empty());
}
SECTION("Check string length") {
REQUIRE(testString.length() == 48);
}
}
Lets compile and run this code:
$ g++ main.cpp test_catch2.cpp -o myFirstCatch2Test
$ ./myFirstCatch2Test
===============================================================================
All tests passed (5 assertions in 2 test cases)
If everything works as expected and no test failure occurs little is printed on
the command line. If you want to see more details add -s
to the call:
./myFirstCatch2Test -s
Here are some more examples regarding catching thrown or not thrown exceptions:
TEST_CASE("Exceptional test case") {
SECTION("Safty first, no exceptions") {
REQUIRE_NOTHROW(5 == 5);
}
SECTION("Throw exceptions") {
REQUIRE_THROWS(throw std::exception());
REQUIRE_THROWS_AS(throw std::exception(), std::exception);
}
}
1 Sometimes you are stick to an old C++98 compiler and toolchain. I also had the pleasure to work with such old compilers. This is hard enough. So never judge anybody who has to take care about the old messed code and the old nasty compilers!
2 That's why you always should limit the use of macros to the absolutley necessary minumum when writing C++. Use the great modern functionalities the C++-responsibles created for you!
3 Never use the shortcuts used in prototypes for your projects which will be (eventually) shipped to customers (real customers, friends, collegues, enemies). It will make maintenance and extensibility realy hard and drive you or your next ones mad!