Assert macros (C++ SDK)#
Assert macros define test properties about your software or workload. They are part of the Antithesis C++ SDK.
Every macro takes a parameter message, which is a human-readable identifier for assertions; and a parameter details, which allows you to allows you to record additional contextual information.
Always assertions#
Always assertions assert that every time the assertion is encountered, some condition is true. If a single counterexample to the asserted property is found, the assertion will fail. See the always properties documentation for further details.
ALWAYS(bool condition, const char* message, const JSON& details)
Asserts that condition
is true every time this macro is called, and that it is called at least once. The corresponding test property will be viewable in the Antithesis SDK: Always group of your triage report.
Parameters: See common parameters below.
Example
#include <antithesis_sdk.h>
// ...
int bar(int x, int y){
ALWAYS(x > 0, "x is positive", {{"x", x}});
// Include the value of x to help debug if this fails
}
ALWAYS_OR_UNREACHABLE(bool condition, const char* message, const JSON& details)
Asserts that condition
is true every time this macro is called. The corresponding test property will pass if the assertion is never encountered (unlike Always
assertion types). This test property will be viewable in the “Antithesis SDK: Always” group of your triage report.
This function is similar to assertions in most assertions libraries that you may be familiar with.
Parameters: See common parameters below.
UNREACHABLE(const char* message, const JSON& details)
Asserts that a line of code cannot be reached. The assertion will fail if it is reached, and will pass if it is never reached. Assertions of this form will be grouped in the “Antithesis SDK: Reachability” section of the triage report.
Parameters: See common parameters below.
#include <antithesis_sdk.h>
// The queue should never have more than 100 elements
// So the following code should never be exercised
if (queue_length > 100) {
UNREACHABLE("Queue overflow handling should never be reached", {{"queue_length", queue_length}});
}
ALWAYS_GREATER_THAN(T left, T right, const char* message, const JSON& details)
ALWAYS_GREATER_THAN_OR_EQUAL_TO(T left, T right, const char* message, const JSON& details)
ALWAYS_LESS_THAN(T left, T right, const char* message, const JSON& details)
ALWAYS_LESS_THAN_OR_EQUAL_TO(T left, T right, const char* message, const JSON& details)
Equivalent to asserting ALWAYS(left > right, message, details)
etc. Information about left
and right
will automatically be added to the details
parameter, with keys left
and right
. If you use these macros for assertions that compare numeric quantities, you may help Antithesis find more bugs.
Parameters: T
is numeric. See also common parameters below.
Example
#include <antithesis_sdk.h>
// ...
int bar(int x, int y){
ALWAYS_GREATER_THAN(x, 0, "x is positive", {});
// The value of x will automatically be included in 'details'
}
ALWAYS_SOME(std::vector<std::pair<string, bool>> named_bools, const char* message, const JSON& details)
Asserts that every time the macro is called, at least one bool in named_bools
is true. Equivalent to ALWAYS(named_bools[0].second || named_bools[1].second || ..., message, details)
. If you use these macros for assertions about the behavior of booleans, you may help Antithesis find more bugs. Information about named_bools
will automatically be added to the details
parameter, and the keys will be the names of the bools.
Parameters: See common parameters below. Note that the NAMED_LIST
utility macro is provided to pass the named_bools
argument more conveniently.
Example
#include <antithesis_sdk.h>
// You have a variable my_data
// Always, my_data should be stored in at least one replica
ALWAYS_SOME(NAMED_LIST(replica1.contains(my_data), replica2.contains(my_data), replica3.contains(my_data)), "Data is always in some replica", {})
Sometimes assertions#
Sometimes assertions assert that at least one time the assertion is encountered, some condition is true. If a single example of the asserted property is found, the assertion will pass. See why you should use sometimes properties for further details.
SOMETIMES(bool condition, const char* message, const JSON& details)
Asserts that condition
is true at least one time that this macro was called. (If the assertion is never encountered, the test property will therefore fail.) This test property will be viewable in the “Antithesis SDK: Sometimes” group.
Parameters: See common parameters below.
Example
#include <antithesis_sdk.h>
// A function to square a number
// Your tests should sometimes test negative inputs
float squareNumber(float x){
SOMETIMES(x < 0, "Input to squareNumber is negative", {{"x", x}})
return x*x
}
REACHABLE(const char* message, const JSON& details)
Assert that a line of code is reached at least once. The assertion will pass if it is reached at least once. (If it is never reached, it will therefore fail.) The resulting property will be grouped in the “Antithesis SDK: Reachability” section of your triage report.
Parameters: See common parameters below.
Example
#include <antithesis_sdk.h>
// Some sort of retry logic
// Your tests should exercise this functionality at least once
void retryTransaction{
REACHABLE("Retry logic is reachable")
// More code
}
SOMETIMES_GREATER_THAN(T left, T right, const char* message, const JSON& details)
SOMETIMES_GREATER_THAN_OR_EQUAL_TO(T left, T right, const char* message, const JSON& details)
SOMETIMES_LESS_THAN(T left, T right, const char* message, const JSON& details)
SOMETIMES_LESS_THAN_OR_EQUAL_TO(T left, T right, const char* message, const JSON& details)
Equivalent to asserting SOMETIMES(T left > T right, ...)
, etc. Information about left
and right
will automatically be added to the details
parameter, with keys left
and right
. If you use these macros for assertions that compare numeric quantities, you may help Antithesis find more bugs.
Parameters: T
is numeric. See also common parameters below.
SOMETIMES_ALL(std::vector<std::pair<string, bool>> named_bools, const char* message, const JSON& details)
Asserts that at least one time the macro is called, every bool in named_bools
is true. Equivalent to SOMETIMES(named_bools[0].second && named_bools[1].second && ..., message, details)
. If you use these macros for assertions about the behavior of booleans, you may help Antithesis find more bugs. Information about named_bools
will automatically be added to the details
parameter, and the keys will be the names of the bools.
Parameters: See common parameters below. Note that the NAMED_LIST
utility macro is provided to pass the named_bools
argument more conveniently.
#include <antithesis_sdk.h>
// When testing, sometimes all of the replicas should be alive
SOMETIMES_ALL(NAMED_LIST(replica1.isAlive(), replica2.isAlive(), replica3.isAlive()), "Sometimes all replicas are alive", {})
Common parameters#
- message
const char* that must be known at compile time. A human readable identifier used to aggregate assertions. Antithesis generates one test property per unique
message
and this test property will be namedmessage
in triage reports.This test property either passes or fails, which depends upon the evaluation of every assertion that shares its
message
. Different assertions in different parts of the code should have differentmessage
, but the same assertion should always have the samemessage
even if it is moved to a different file.- details
JSON. Optional additional information provided by the user to add context for assertion failures. The information that is logged will appear in a triage report, under the details section of the generated property.
Normally the values passed to
details
are evaluated at runtime. If you do not wish to providedetails
, then pass{}
as the parameter.The type of
details
isstd::map<std::string, antithesis::ValueType>
whereantithesis::ValueType
is astd::variant
consisting of many common types, including a nested JSON type.You can specify
details
using initialization syntax:{ {"name", "Bob"}, {"number", 123.4} }
You can also include nested JSON in
details
, which requires the identifierantithesis::JSON
:{ {"nested", antithesis::JSON{ {"a", "b"}, {"c", 1234} } } }
Utility macros#
The macro NAMED_LIST
is provided to conveniently pass arguments to boolean assertions such as SOMETIMES_ALL
and ALWAYS_SOME
. It can also be used to pass the details
argument.
NAMED_LIST(foo, bar, baz)
expands to { { "foo", foo }, { "bar", bar }, { "baz", baz } }
.
NAMED_LIST(dog.say("woof"), cat.say("meow"))
expands to { { "dog.say(\"woof\")", dog.say("woof") }, { "cat.say(\"meow\")", cat.say("meow") } }
.
The macro takes between 1 and 10 parameters (inclusive). Contact us if you require the macro to support more parameters.
Additional Considerations#
Assertions can be added to template definitions. However, you should ensure that the assertion identifier message
is distinct for each instantiation of the template.
We recommend using the __PRETTY_FUNCTION__
macro, which shows the actual name of the instantiated function or method. This should be used in message
to ensure that each instantiation’s assertion has a unique identifier:
#include <antithesis_sdk.h>
template<typename T>
bool is_in_range(T val, T min_val, T max_val) {
bool in_range = ((val >= min_val) && (x <= max_val));
ALWAYS(in_range, "Is in range: " __PRETTY_FUNCTION__, {});
return in_range;
}
Alternatively, __PRETTY_FUNCTION__
can be included in the details
associated with the assertion:
#include <antithesis_sdk.h>
template<typename T>
bool is_in_range(T val, T min_val, T max_val) {
bool in_range = ((val >= min_val) && (x <= max_val));
ALWAYS(in_range, "Is in range", {{"where", __PRETTY_FUNCTION__}});
return in_range;
}