Unit testing contracts#

Testing contracts is tricky as violations often result in the program execution being abruptly terminated rendering it quite difficult to test without death test support by the testing framework.

To simplify this situation, we provide special macros for unit testing with Google Test (other frameworks can be easily added) that do not require the use of death tests.

CHECK_VIOLATES_CONTRACT(call)#

An alias for ASSERT_VIOLATES_CONTRACT(), checks that the statement produces a contract violation and if not, the test is immediately terminated.

If the statement does not produce a violation contract, execution is aborted.

Note

The implementation of this macro is not thread safe.

Parameters:
  • call – the statement to test.

EXPECT_VIOLATES_CONTRACT(call)#

Expect the statement to produce a contract violation. If the statement does not produce a contract, execution still continues.

Use EXPECT when the condition should hold, but in cases where it does not we can still get value out of continuing the test. The test will still ultimately fail at the end, though.

Note

The implementation of this macro is not thread safe.

Parameters:
  • call – the statement to test.

ASSERT_VIOLATES_CONTRACT(call)#

Assert that the statement produces a contract violation. If the statement does not produce a contract violation contract, the test is immediately terminated.

Use ASSERT_VIOLATES_CONTRACT when the condition must hold - if it does not, the test stops right there. Use this when the remainder of the test does not have semantic meaning without this condition holding.

Note

The implementation of this macro is not thread safe.

Parameters:
  • call – the statement to test.

Example#

// Some function to be tested in some .cpp file
auto TestExpectDefault(const int *ptr) -> int {
  ASAP_EXPECT(ptr);
  return *ptr;
}
#include "contract/ut/framework.h"
#include "contract/ut/gtest.h"

#include <gtest/gtest.h>

TEST(GoogleTestDeathMacros, DefaultModeExpectDeath) {
  CHECK_VIOLATES_CONTRACT(testing::TestExpectDefault(nullptr));
}

auto main(int argc, char **argv) -> int {
  asap::contract::PrepareForTesting();
  ::testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}

Limitations#

The mechanism that allows contract checks to be tested during unit tests is implemented with setjmp and longjmp. It uses global variables to save the stack environment during the setjmp/longjmp which is not thread safe.