Catch2

Tutorial

Like most test frameworks, Catch2 supports a class-based fixture mechanism, where individual tests are methods on class and setup/teardown can be done in constructor/destructor of the type.

However, their use in Catch2 is rare, because idiomatic Catch2 tests instead use sections to share setup and teardown code between test code. This is best explained through an example (code):

TEST_CASE( "vectors can be sized and resized", "[vector]" ) {
    std::vector<int> v( 5 );
 
    REQUIRE( v.size() == 5 );
    REQUIRE( v.capacity() >= 5 );
 
    SECTION( "resizing bigger changes size and capacity" ) {
        v.resize( 10 );
 
        REQUIRE( v.size() == 10 );
        REQUIRE( v.capacity() >= 10 );
    }
    SECTION( "resizing smaller changes size but not capacity" ) {
        v.resize( 0 );
 
        REQUIRE( v.size() == 0 );
        REQUIRE( v.capacity() >= 5 );
    }
    SECTION( "reserving bigger changes capacity but not size" ) {
        v.reserve( 10 );
 
        REQUIRE( v.size() == 5 );
        REQUIRE( v.capacity() >= 10 );
    }
    SECTION( "reserving smaller does not change size or capacity" ) {
        v.reserve( 0 );
 
        REQUIRE( v.size() == 5 );
        REQUIRE( v.capacity() >= 5 );
    }
}

For each SECTION the TEST_CASE is executed from the start. This means that each section is entered with a freshly constructed vector v, that we know has size 5 and capacity at least 5, because the two assertions are also checked before the section is entered. Each run through a test case will execute one, and only one, leaf section.

  • REQUIRE: Stop at failure
  • CHECK: Continue at failure

Google Test

Test Fixtures: Using the Same Data Configuration for Multiple Tests

To create a fixture:

  1. Derive a class from ::testing::Test . Start its body with protected:, as we’ll want to access fixture members from sub-classes.
  2. Inside the class, declare any objects you plan to use.
  3. If necessary, write a default constructor or SetUp() function to prepare the objects for each test. A common mistake is to spell SetUp() as Setup() with a small u - Use override in C++11 to make sure you spelled it correctly.
  4. If necessary, write a destructor or TearDown() function to release any resources you allocated in SetUp() . To learn when you should use the constructor/destructor and when you should use SetUp()/TearDown(), read the FAQ.
  5. If needed, define subroutines for your tests to share.
class QueueTest : public ::testing::Test {
 protected:
  void SetUp() override {
     q1_.Enqueue(1);
     q2_.Enqueue(2);
     q2_.Enqueue(3);
  }
 
  // void TearDown() override {}
 
  Queue<int> q0_;
  Queue<int> q1_;
  Queue<int> q2_;
};
 
TEST_F(QueueTest, IsEmptyInitially) {
  EXPECT_EQ(q0_.size(), 0);
}
 
TEST_F(QueueTest, DequeueWorks) {
  int* n = q0_.Dequeue();
  EXPECT_EQ(n, nullptr);
 
  n = q1_.Dequeue();
  ASSERT_NE(n, nullptr);
  EXPECT_EQ(*n, 1);
  EXPECT_EQ(q1_.size(), 0);
  delete n;
 
  n = q2_.Dequeue();
  ASSERT_NE(n, nullptr);
  EXPECT_EQ(*n, 2);
  EXPECT_EQ(q2_.size(), 1);
  delete n;
}

The rule of thumb is to use EXPECT_* when you want the test to continue to reveal more errors after the assertion failure, and use ASSERT_* when continuing after failure doesn’t make sense.