GTest Basics and Effective Practices

Exploring the GTest Library and Common Use Cases

Kyle Hurd


Why Test?


A Contract to Future Developers

Requirement changes will be introduced in the future.


Tests Decrease the Chances of Breaking Previously Working Behavior

gitGraph commit commit branch feature1 checkout feature1 commit commit checkout main merge feature1 commit commit branch feature2 checkout feature2 commit checkout main merge feature2

What to Test?


Testing Frequently Modified Areas of Code

Frequently touched regions are at greater risk of unintended changes in behavior.


Test Critical Regions of Code

Critical regions of code should be thoroughly tested to avoid encountering bugs when using them.


Test (Preferably) Every Class

Unit testing each class or free function increases code coverage and reduces odds of overlooked bugs.


Test Public Methods

Generally, only test public methods in a class.


Test Branching Conditions

Consider all paths a function can branch to and make sure to test each branching condition.


How I Like to Structure Test Cases

AAA Pattern

Arrange: Set up and prepare the state of the test. Act: Call the function. Assert: Test the outcome or new state of the instance under test.


AAA Pattern

Consider we want to test the following function.

1
2
template <typename T, std::enable_if_t<!std::is_integral_v<T>, bool> = true>
double translate(T num);

AAA Pattern

1
2
3
TEST(MathTests, CanTranslate) {
  EXPECT_EQ(translate(4.32), 23.4);
}

AAA Pattern

1
2
3
4
5
6
7
8
9
10
11
TEST(MathTests, CanTranslate) {
  // Arrange
  constexpr auto input{ 4.32 };
  constexpr auto expected_output{ 23.4 };

  // Act
  const auto actual_output{ translate(input) };

  // Assert
  EXPECT_EQ(expected_input, actual_output);
}

AAA Pattern

Testing a class with dependencies.

1
2
3
4
5
6
7
8
9
10
class MyClass {
public:
  MyClass(IDependencyA* depA, IDependencyB* depB)
    : m_dependencyA(depA), m_dependencyB(depB) {}

  int run();
private:
  IDependencyA* m_dependencyA = nullptr;
  IDependencyB* m_dependencyB = nullptr;
};

AAA Pattern

1
2
3
4
5
6
7
8
9
10
11
12
class MyClassTests : public Test {
protected:
  MyClassTests() {
    m_depA = std::make_unique<NiceMock<MockDependencyA>>();
    m_depB = std::make_unique<NiceMock<MockDependencyB>>();
    m_classUnderTest = MyClass(m_depA.get(), m_depB.get());
  }

  std::unique_ptr<MockDependencyA> m_depA;
  std::unique_ptr<MockDependencyB> m_depB;
  MyClass m_classUnderTest;
};

AAA Pattern

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
TEST_F(MyClassTests, RunCallsDependencyAAndB) {
  // Arrange
  constexpr auto expected_output{ 1 };
  EXPECT_CALL(*m_depA, get(_)).WillOnce(
    Return(std:string())
  );
  EXPECT_EQ(*m_depB, create(_)).WillOnce(
    Return(std::string())
  );

  // Act
  const auto actual_output{ m_classUnderTest.run() };

  // Assert
  EXPECT_EQ(actual_output, expected_output);
}

Assert VS Expect

ASSERT_* - Fails and ends the test immediately if condition is not met.

EXPECT_* - Fails the test but allows test completion.


Assert VS Expect

Ex. Connecting to a database is required to continue.

1
2
3
4
5
6
7
8
9
10
11
// Arrange
MyClass myClass;
ASSERT_TRUE(myClass.connectDB()); // If cannot establish connection, cannot test code

// Act
const auto actual{ myClass.sendRequest() };

// Assert
EXPECT_EQ(actual.value, "Expected");
EXPECT_EQ(actual.primaryKey, "Primary Key");
EXPECT_EQ(actual.name, "Name");

Assert VS Expect

Ex. Requiring a container is a specific size.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Arrange
MyClass myClass;

// Act
const auto actual{ myClass.sendRequest() };

// Assert
ASSERT_EQ(actual.container.size(), 4); // End test if size is not 4.

EXPECT_EQ(actual.container.at(0), "Value 1");
EXPECT_EQ(actual.container.at(1), "Value 2");
EXPECT_EQ(actual.container.at(2), "Value 3");
EXPECT_EQ(actual.container.at(3), "Value 4");


Types of Comparison Assertions


Types of Boolean Assertions


Types of Near Assertions


1
EXPECT_NEAR(20.123456, 20.123000, 1e-3);

The above code evaluates to true (only compares to the thousandths position).


Types of Throw Assertions


Displaying Good Error Messages

GTest displays error values very well:

1
2
3
4
5
Expected equality of these values:
  x
    Which is: 5
  y
    Which is: 10

You can return custom messages on failure:

1
2
3
EXPECT_TRUE(<false_condition>) <<
  "Expected " << <true_condition> <<
  "but instead got " << <false_condition> << '.';

Types of Google Tests


Example of Parametric Tests on Ccmath Library

Link