What is GoogleTest
Googletest is a testing framework to help write better C++ code
- supports any kind of tests, not just unit tests
- based on the popular xUnit (e.g. JUnit, PyUnit) architecture
What defines a “good” test
A good test should:
- Be independent and repeatable
- Be well-organized and reflect the structure of the tested code
- => gtest groups related tests into test suits that can share data and subroutines
- Be portable and reusable => platform neutral, compiler neutral
- Verbose diagnostic information when test fail
- => gtest does not stop at the first test failure but stops the current test and continues with the rest tests if any.
- => enable users to detect and fix multiple bugs in a single run-edit-compile cycle
- => gtest does not stop at the first test failure but stops the current test and continues with the rest tests if any.
- Liberate test writers from housekeeping chores and focus on the test content
- => gtest automatically keeps track of all tests defined and does not require users to enumerate them in order to run them
- Be fast
- => gtest enable users to reuse shared resources across tests and pay for the setup/tear-down only once, without making tests depend on each other
The nomenclature
- Test: gtest’s term for “Test Case” in ISTQB 1. Exercise a particular program path with specific input values and verify the results
- Test Suit: a group of related tests
- Test Program: contains one or more test suits
Basic Concepts
The usage of GoogleTest starts with writing assertions.
- An assertion is a statement that checks whether a condition is true or false.
- Assertion result can be success, nonfatal failure, fatal failure.
- A fatal failure aborts the current function
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
-
GTest assertions are macros that resemble function calls.
-
When assertion fails, gtest prints the assertion’s source file, line number, along with a failure message (user can supply a custom failure message which will be appended to gtest’s msg)
-
Two types of assertions that differ in their effects on the code being tested
ASSERT_*
generate fatal failures when they fail and abort the current function. The abortion may skip clean-up code that comes after it and hence may cause a space leak! (particularly if you will also use a heap checker)EXPECT_*
generate non-fatal failures, which don’t abort the current function and allow more than one failure to be reported in a test
-
Syntax
ASSERT_EQ(x.size(), y.size()) << "Vectors x and y are of unequal length"; // Anything that can be streamed to an ostream can be streamed to an assertion macro for (int i = 0; i < x.size(); ++i) { EXPECT_EQ(x[i], y[i]) << "Vectors x and y differ at index " << i; }
-
Basic Assertions
Fatal Assertions Nonfatal Assertions Explaination ASSERT_TRUE(cond)
EXPECT_TRUE(cond)
cond
is trueASSERT_FALSE(cond)
EXPECT_FALSE(cond)
cond
is false -
Binary Comparisons
- N.B. value arguments (
val1
,val2
) must be comparable by the assertion’s comparison operator (==
,<
, etc.) or you’ll get a compilation error. ASSERT_EQ(val1, val2)
,ASSERT_NE(val1, val2)
, etc.EXPECT_EQ(val1, val2)
,EXPECT_NE(val1, val2)
, etc.- You may need to use
ASSERT_TRUE
orEXPECT_TRUE
to assert the equality of two objects of an user-defined type based on the Google C++ Style Guide- but when possible,
ASSERT_EQ
is preferred toASSERT_EQ(val1 == val2)
since it tells youval1
andval2
’s values on failure
- but when possible,
- N.B. value arguments (
-
- use
ASSERT_STREQ()
rather thanASSERT_EQ
- to assert that a C string is NULL, use
ASSERT_STREQ(c_str, NULL)
orASSERT_EQ(c_str, nullptr)
if c++11 is supported
- use
-
Pointer Comparisons
- use
*_EQ(ptr, nullptr)
and*_NE(ptr, nullptr)
instead of*_EQ(ptr, NULL)
. (becausenullptr
is typed)
- use
-
Floating point Comparisons
- use the floating point variations of some of these macros in order to avoid rounding problems (see here for more information)
Simple Tests
-
Use the
TEST()
macro to define and name a test function- test functions are ordinary C++ functions that don’t return a value
-
Use various gtest assertions we discussed above in the test function, along with any other valid C++ statements you want to use
-
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
-
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
-
Test’s full name consists of its containing test suite and its individual test name
-
Tests from different test suites can have the same individual name
-
Test results are grouped by test suites, and logically related tests should be in the same test suite.
-
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
- Derive a class from
::testing::Test
and start its body withprotected
since we want to access the fixture members from sub-classes - Inside the class, declare any objects you plan to use
- If necessary, write a default constructor or
SetUp()
function to prepare the objects for each test. Useoverride
in C++11 to make sure you spelled SetUp correctly! - If necessary, write a destructor or
TearDown()
function to release any resources you allocated inSetUp()
. - If needed, define subroutines for your tests to share
How to use a fixture
-
use
TEST_F()
instead ofTEST()
as it allows you to access objects and subroutines in the test fixture -
the first argument defines the test suite name, and it must be the name of the test fixture class
-
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
-
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
- immediately initialize it via
-
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
-
Naming convention for fixture classes are: append the
Test
to the class name. e.g. give it the nameFooTest
if you want to test classFoo
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
TEST()
andTEST_F()
implicitly register their tests with gtest- You can run them with
RUN_ALL_TESTS()
, which returns 0 if all the tests are successful or 1 otherwise. RUN_ALL_TESTS()
runs all tests in your link unit and they can be from different test suites, or even different source files- You should call
RUN_ALL_TESTS()
only once! Calling it more than once conflicts with some advanced gtest features and thus is not supported
Behind the scenes, the RUN_ALL_TESTS()
macro:
- Saves the state of all gtest flags
- Creates a test fixture object for the first test
- Initializes it via
SetUp()
- Runs the test on the fixture object
- Cleans up the fixture via
TearDown()
- Deletes the fixture
- Restores the state of all gtest flags
- Repeats the above steps for the next test, until all tests have run
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.
-
SUCCEED()
- it does not make the overall test succeed and a test is considered successful only if none of its assertions fail during its execution
- it is purely documentary and currently does not generate any user-visible output
-
FAIL()
,ADD_FAILURE()
, andADD_FAILURE_AT()
FAIL()
generates a fatal failureADD_FAILURE()
andADD_FAILURE_AT()
generate nonfatal failure- they are useful when the control flow (rather than the boolean expression) determines the test’s sucecss or failure
FAIL()
can only be used in functions that returnvoid
e.g.
switch(expression) { case 1: ... some checks ... case 2: ... some other checks ... default: FAIL() << "We shouldn't get here."; }
Exception Assertions
These assertions are for verifying that a piece of code throws or does not throw an exception of the given type.
Fatal | Nonfatal | Explaination |
---|---|---|
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
-
define a fixture class that derived from both
testing::Test
andtesting::WithParamInterface<T>
whereT
is the type of your parameter values. For convenience, you can just derive the fixture class fromtesting::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. -
If your test fixture defines
SetUpTestSuite()
orTearDownTestSuite()
they must be declaredpublic
rather thanprotected
in order to useTEST_P
. -
Use the
TEST_P
macro to define as many test patterns using this fixture as you want -
Inside the test, you can access the parameter by calling
GetParam()
method of theTestWithParam<T>
class -
You can then instantiate the test suite with any set of parameters using
INSTANTIATE_TEST_SUITE_P()
-
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"));