Skip to content

GoogleTest Primer

Published: at 01:00 AM

What is GoogleTest

Googletest is a testing framework to help write better C++ code

What defines a “good” test

A good test should:

The nomenclature

Basic Concepts

The usage of GoogleTest starts with writing assertions.

Tests use assertions to verify the tested code’s behavior. If a test crashes or has a failed assertion, then this test fails.

A Test Suit contains one or many tests. It’s usually a group of tests that reflect the structure of the tested code. Tests within a test suite can share common resources (objects, subroutines) by putting them into a test fixture class

Assertions

Simple Tests

  1. Use the TEST() macro to define and name a test function

    • test functions are ordinary C++ functions that don’t return a value
  2. Use various gtest assertions we discussed above in the test function, along with any other valid C++ statements you want to use

  3. Test result is determined by the assertions.

    • if any assertion in the test fails (either fatally or non-fatally), or if the test crashes, the entire test fails
  4. Arguments

    • first argument is the test suite name
    • second argument is the test’s name within the test suite
    • both should be valid C++ identifiers and should not contain any underscores
  5. Test’s full name consists of its containing test suite and its individual test name

  6. Tests from different test suites can have the same individual name

  7. Test results are grouped by test suites, and logically related tests should be in the same test suite.

  8. Naming convention for test suits and tests should follow the same convention for naming functions and classes

    e.g.

    TEST(TestSuiteName, TestName) {
      ... test body ...
    }

Test Fixtures

Enable users to use the same data configuration for multiple tests.

Steps to create a fixture

  1. Derive a class from ::testing::Test and start its body with protected since we want to access the 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. Use override in C++11 to make sure you spelled SetUp correctly!
  4. If necessary, write a destructor or TearDown() function to release any resources you allocated in SetUp().
  5. If needed, define subroutines for your tests to share

How to use a fixture

  1. use TEST_F() instead of TEST() as it allows you to access objects and subroutines in the test fixture

  2. the first argument defines the test suite name, and it must be the name of the test fixture class

  3. you must first define the test fixture class before you can actually use it in your tests! or you will get the compiler error virtual outside class declaration

  4. gtest will create a fresh test fixture at runtime for each test defined with TEST_F(), then

    • immediately initialize it via SetUp()
    • run the test, clean up by calling TearDown()
    • delete the test fixture
  5. Note that different tests in the same suite have different test fixture objects, and gtest always delete a test fixture before it creates the next one

    • gtest does not reuse the same test fixture for multiple tests
    • any changes one test makes to the fixture do not affect other tests
  6. Naming convention for fixture classes are: append the Test to the class name. e.g. give it the name FooTest if you want to test class Foo

    e.g.

    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;
    }

Invoking the tests

Behind the scenes, the RUN_ALL_TESTS() macro:

If a fatal failure happens, the subsequent steps will be skipped!

Writing the main() function

Most users should not need to write their own main function and instead they should link with gtest_main(), which defines a suitable entry point.

When you want to do something before the tests that cannot be expressed within the framework of fixtures and test suits, then you can consider writing your own main function.

If you write your own main function, it should return the value of RUN_ALL_TESTS()!

See the starter code here.

Advanced gtest topics

Explicit Success and Failure macros

SUCCEED(), FAIL() do not actually test a value or expression and they generate a success or failure directly. They supports streaming of custom messages into them.

Exception Assertions

These assertions are for verifying that a piece of code throws or does not throw an exception of the given type.

FatalNonfatalExplaination
ASSERT_THROW(statement, exception_type)EXPECT_THORW(statement, exception_type)statement throws an exception of the given type
ASSERT_ANY_THROW(statement)EXPECT_ANY_THROW(statement)statement throws an exception of any type
ASSERT_NO_THROW(statement)EXPECT_NO_THROW(statement)statement does not throw any exception

Value-Parameterized Tests

Enable users to test code with different parameters without writing multiple copies of the same test.

HowTo

  1. define a fixture class that derived from both testing::Test and testing::WithParamInterface<T> where T is the type of your parameter values. For convenience, you can just derive the fixture class from testing::TestWithParam<T>, which itself is derived from both the required classes. T can be any copyable type and if it is a raw pointer, you are responsible for managing the lifespan of the pointed values.

  2. If your test fixture defines SetUpTestSuite() or TearDownTestSuite() they must be declared public rather than protected in order to use TEST_P.

  3. Use the TEST_P macro to define as many test patterns using this fixture as you want

  4. Inside the test, you can access the parameter by calling GetParam() method of the TestWithParam<T> class

  5. You can then instantiate the test suite with any set of parameters using INSTANTIATE_TEST_SUITE_P()

  6. You must place the INSTANTIATE_TEST_SUITE_P() statement at global or namespace scope, rather than function scope.

    e.g.

    INSTANTIATE_TEST_SUITE_P(InstantiationName,
                             FooTest,
                             testing::Values("meeny", "miny", "moe"));

Footnotes

  1. https://glossary.istqb.org/en/search/test%20case