GoogleTest

Introduction

  • Test Suite: Group of one or more tests. When multiple tests in a test suite need to share common objects and subroutines, you can put them into a test fixture class.

  • Test: Exercise a particular program path with specific input values and verify the results

  • Test Fixture: Contain multiple tests that need to share common objects and subroutines.

Assertions: Statements that check whether a condition is true (Assertion Reference here).

  • Result can be success, nonfatal or fatal failure.
  • If fatal, aborts the function being tested.

Assertion are macros that can have different effects on the function:

  • ASSERT_*: Generates fatal failures and abort the current function.
  • EXPECT_*: Generates nonfatal failures which do not abort the current function.

To show a custom failure message, it is possible to stream it to an assertion:

ASSERT_EQ(x.size(), y.size()) << "Vectors x and y are of unequal length";

for (int i = 0; i < x.size(); ++i) {
  EXPECT_EQ(x[i], y[i]) << "Vectors x and y differ at index " << i;
}

For the complete list of assertions provided by GoogleTest, see the Assertions Reference.

Creating a test

  1. Use the TEST() macro to define and name a test function that does not return a value.
  2. Along with valid C++ statements, use assertions to check values
  3. Results are determined by assertions.

     TEST(TestSuiteName, TestName) {			--> Names should be valid C++ identifiers
         ... test body ...											and not contain underscores (_)
     }																		--> Same TestSuiteName tests are grouped
                                                                                     (they belong to the same test suite)
    

Example:

int Factorial(int n);  // Returns the factorial of n

 // Tests factorial of 0.
TEST(FactorialTest, HandlesZeroInput) {
  EXPECT_EQ(Factorial(0), 1);
}

// Tests factorial of positive numbers.
TEST(FactorialTest, HandlesPositiveInput) {
  EXPECT_EQ(Factorial(1), 1);
  EXPECT_EQ(Factorial(2), 2);
  EXPECT_EQ(Factorial(3), 6);
  EXPECT_EQ(Factorial(8), 40320);
}

A test fixture allows for multiple tests that use the same configurations to reuse that configuration code:

#include <gtest/gtest.h>

class SampleTest : public ::testing::Test {

protected:					<-- protected since the members are accessed via sub-classes
    void SetUp() override {
	std::cout << "Test up\n";		<-- I added this but standard output might not be always visible
        // Code here will be executed immediately before each TEST_F
    }
    void TearDown() override {
 		std::cout << "Test down\n";		<-- I added this but standard output might not be always visible
        // Code here will be executed immediately after each TEST_F
    }
    // Define your test object here, such as:
    int testVariable = 10;
};


TEST_F(SampleTest, TestName) {		<-- Must be a TEST_F (_F stands for "fixture")
    // Access your test object		    (allows access to objects and subroutines)
    EXPECT_EQ(testVariable, 10);	    First argument must be name of fixture class
    // Other test code...
}

In this case, TestName is based on the SampleTest test fixture class and uses the SetUp and TearDown methods from it. Sometimes, the constructor/destructor of the test fixture should be used instead of SetUp/TearDown (see more here).

Running tests

After defining your tests, you can run them with RUN_ALL_TESTS(), which returns 0 if all the tests are successful, or 1 otherwise. Note that RUN_ALL_TESTS() runs all tests in your link unit–they can be from different test suites, or even different source files.

gMock

gMock is a library or “framework” for creating mock classes for C++ that is included with googleTest.

A mock object implements the same interface as a real object (to be used instead) AND specify at runtime how it will be used and called (which methods, arguments, how many times, etc).

There is a difference between fake objects and mock objects:

  • Fake objects have “cheap” working implementations which are not suitable for production
  • Mocks are pre-programmed with expectations, which form the specification for what is expected.

Creating a Mock Class

  1. Derive a class MockTurtle from Turtle
  2. Take a virtual function of Turtle (gMock overrites the function from a base class with a virtual function automatically)
  3. In public: section, write MOCK_METHOD()
  4. Take the function signature (inputs, outputs and name of function) and paste it into the macro with commas separating the fields
    • If mocking a const method, add a forth parameter (const)
  5. Since the virtual method is being overriding, it is suggested to add the override keyword: (const, override)
  6. Do step 2 to 5 for every virtual function being mocked

     #include <gmock/gmock.h>  // Brings in gMock.
     class MockTurtle : public Turtle {														// #1 & #2
         public:																											// #3
             ...
             MOCK_METHOD(void, PenUp, (), (override));									// #4 & #5
             MOCK_METHOD(void, PenDown, (), (override));
             MOCK_METHOD(void, Forward, (int distance), (override));
             MOCK_METHOD(void, Turn, (int degrees), (override));
             MOCK_METHOD(void, GoTo, (int x, int y), (override));
             MOCK_METHOD(int, GetX, (), (const, override));
             MOCK_METHOD(int, GetY, (), (const, override));
     };
    

Using the mocks in tests

  1. Import the gMock names from the testing namespace (namespaces are a good idea)
  2. Create some mock objects
  3. Specify expectations (how many times will it be called, what arguments, etc.)
  4. Write code that uses the mocks
    • Optionally, check results with googletest assertions (if mock is called more than expected or with wrong arguments)
  5. When a mock is destroyed, gMock will check if expectations have been satisfied

     #include "path/to/mock-turtle.h"
     #include <gmock/gmock.h>
     #include <gtest/gtest.h>
    	
     using ::testing::AtLeast;                         // #1
    	
     TEST(PainterTest, CanDrawSomething) {
       MockTurtle turtle;                              // #2
       EXPECT_CALL(turtle, PenDown())                  // #3
           .Times(AtLeast(1));
    	
       Painter painter(&turtle);                       // #4
    	
       EXPECT_TRUE(painter.DrawCircle(0, 0, 10));      // #5
     }
    

Setting Expectations

The right expectations must be set for the test to be evaluated correctly and not fail as result of unrelated changes. In gMock the EXPECT_cALL() is used to set an expectation on a mock method. The cardinality options can be seen here:

using ::testing::Return;
...
EXPECT_CALL(turtle_object, GetX(matchers))     // Two arguments: the mock object and the method and its arguments 
    .Times(5)                                  // Method will be called 5 times (cardinality),
    .WillOnce(Return(100))                     // it will return 100 one time (the first time),
    .WillOnce(Return(150))                     // it will return 150 the second time
    .WillRepeatedly(Return(200));              // and then return 200 the remaining times.

Another examples:

  1. Specifying expected arguments:

    These can work by setting any generic comparison, as seen here.

     // Expects the turtle to move forward by 100 units. (Equivalent to Eq(100))
     EXPECT_CALL(turtle, Forward( 100 ) );
       
     // Expects the turtle moves forward by at least 100 units.
     EXPECT_CALL(turtle, Forward( Ge(100) ) );
    
  2. Not interested in the value of certain arguments:

    For this, the _ can be used in place of an argument, meaning “anything goes” (Wildcard). The _ is called a matcher and can be used inside EXPECT_CALL().

     using ::testing::_;
     ...
     // Expects that the turtle jumps to somewhere on the x=50 line.
     EXPECT_CALL(turtle, GoTo( 50, _ ) );
    

TODO: See if there is a reference to the 3 A’s (Arrange Act Assert)

Interesting stuff