Google Test Matchers - Part 2

Tom Hulton-Harrop | January 25, 2023

Google Test Matchers - Part 2

Topic

Continuing the introduction to Google Test matchers. Please see Part 1 for more information.

Motivation

Shorter, clearer tests that take the boilerplate and fragility out of testing.

Examples

Math types

Matchers can be very useful when writing unit tests for math types (which nearly always require some sort of epsilon check). We know to use EXPECT_NEAR when testing floating point values but this can get unwieldy when working with vector or matrix types. We should not do this:

EXPECT_TRUE(positionA.IsClose(positionB))

If this test fails we get no useful output (what values were positionA and positionB?). We’d have to update the code or use the debugger to get any idea. If we’d instead written this:

EXPECT_THAT(positionA, IsClose(positionB))

Where IsClose is defined as:

MATCHER_P(IsClose, expected, "")
{
    if (arg.IsClose(expected))
    {
        return true;
    }

    return false;
}

If the test fails, the object will be printed to the test output. By default this is just the address of the object (e.g. 12-byte object <00-00 80-3F 00-00 80-3F 00-00 80-3F>) but it’s possible to overload the << operator for any custom type and now when the test fails you see a friendly human-readable value.

std::ostream& operator<<(std::ostream& os, const AZ::Vector3& vec)
{
    return os << "(X: " << vec.GetX() << ", Y: " << vec.GetY() << ", Z: " << vec.GetZ() << ")";
}

//outputs

Expected: is close (X: 2, Y: 2, Z: 2)
Actual: (X: 1, Y: 1, Z: 1).

(note: A lot of these already exist in MathTestHelpers.h)

Collections of math types

Another very useful matcher is called Pointwise which can be used to provide a matcher for each element in a container.

EXPECT_THAT(actualPositions, Pointwise(ContainerIsClose(), expectedPositions));

Where ContainerIsClose is defined as:

MATCHER(ContainerIsClose, "")
{
    const auto& [expected, actual] = arg;
    if (expected.IsClose(actual))
    {
        return true;
    }
    return false;
}

We use a structured binding to extract the actual and expected value and then compare each in turn (arg is part of the MATCHER macro magic). This will work with any type that provides an IsClose member function so can be used with numerous AZ math types.

Further Reading

There’s a great reference guide now available for Google Test that can be found here:

It has lots of useful documentation and examples to get more acquainted with Google Test.

To be continued…

In the last installment we’ll look a little more at writing our own matchers and some more examples of where matchers can save time and effort.

Disclaimer: The views expressed here are those of the individual author and do not represent those of the Open 3D Foundation, Open 3D Engine or individual’s respective company.

Back to blogs