Introduction to Google Mock

C++ Testing and Mocking Framework

Created by Donald Whyte / @donald_whyte
Credit to Denis Cheklov for his contributions

## Outline * Google Test * Why Mock? * Google Mock * Real Life Example * When to Mock
## Google Test
## Google Test Cross-platform C++ testing framework by Google Commonly used in conjunction with Google Mock
## Features * Rich set of assertions * Value-parametrised tests * Type-parametrised tests * Death tests * Test discovery * XML test report generation * Multi-threaded tests

Assertion Macros


ASSERT_EQ(5, 5);
ASSERT_GE(5, 0);
ASSERT_FLOAT_EQ(4.4, 4.4444444287381217);
ASSERT_TRUE(somePtr);
ASSERT_EQ(c1.size(), c2.size())
    << "Vectors c1 and c2 are of unequal length";

ASSERT_THROW(c1.at(47374), std::out_of_range);

ASSERT_DEATH(c1[47374], "Assertion failed:*");
                        

EXPECT_* and ASSERT_*


// assertions abort test if they fail
ASSERT_TRUE(somePtr);
// even if expectations fail, the test continues
// allows multiple failures to be recorded in one test pass
EXPECT_EQ("donald", somePtr->name());
EXPECT_EQ(105, somePtr->age());
                        

Test


TEST(CaseName, TestName) {

}
                        

TEST(StockRepository, ShouldSuccessfullyAddNewStock) {
    StockRepository repo;

    // Ensure stock can't be retrieved initially
    Stock* nonExistentStock = repo.stock("IBM");
    EXPECT_FALSE(nonExistentStock);
    // Add stock
    const int rc = repo.addStock("IBM", 4.74);
    EXPECT_EQ(0, rc);
    // Ensure it can be retrieved
    Stock* addedStock = repo.stock("IBM");
    ASSERT_TRUE(addedStock);
    EXPECT_EQ("IBM", addedStock->name());
    EXPECT_DOUBLE_EQ(4.74, addedStock->price());
}
                        

Test Fixture


class StockRepositoryTests : public ::testing::Test {
  protected:
    StockRepository d_repo;

  public:
    StockRepositoryTests() { } // #1

    ~StockRepositoryTests() { } // #4

    void SetUp() { // #2
        // initialise test data, mocks and objects under test
        d_repo.addStock("AAPL", 50.5);
    }

    void TearDown() { // #3
        // cleanup used resources
    }
};
                        

Test Fixture


TEST_F(StockRepositoryTests, TotalStockCountReflectsNumStocksInRepo) {
    EXPECT_EQ(1u, d_repo.numStocks());
}
                        

TEST_F(StockRepositoryTests, ShouldFailToAddStockWithDuplicateName) {
    // Try to add stock that already exists
    const int rc = d_repo.addStock("AAPL", 242.53);
    EXPECT_NE(0, rc);
    // Ensure price was not overwritten
    Stock* unchangedStock = repo.stock("AAPL");
    ASSERT_TRUE(unchangedStock);
    EXPECT_EQ("AAPL", unchangedStock->name());
    EXPECT_DOUBLE_EQ(50.5, unchangedStock->price());
    // Ensure stock count was not changed
    EXPECT_EQ(1u, d_repo.numStocks());
}
                        

Test Discovery


.
..
src/
tests/
    finprogtests.m.cpp
    Makefile
    finprog_stock.cpp
    finprog_stockrepository.cpp
    ...

                        

// finprogtests.m.cpp
#include <gtest/gtest.h>

int main(int argc, char* argv[]) {
    ::testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}
                        

Running Tests


./finprogtests
[==========] Running 4 tests from 2 test cases.
[----------] Global test environment set-up.
[----------] 3 tests from StockRepositoryTests
[ RUN      ] StockRepositoryTests.TotalStockCountReflectsNumStocksInRepo
[       OK ] StockRepositoryTests.TotalStockCountReflectsNumStocksInRepo (0 ms)
[ RUN      ] StockRepositoryTests.ShouldFailToAddStockWithDuplicateName
[       OK ] StockRepositoryTests.ShouldFailToAddStockWithDuplicateName (1 ms)
[----------] 2 tests from StockRepositoryTests (1 ms total)

[----------] 2 tests from StockTests
[ RUN      ] StockTests.InitialisedCorrectly
[       OK ] StockTests.InitialisedCorrectly (1 ms)
[ RUN      ] StockTests.PrintSuccessful
[       OK ] StockTests.PrintSuccessful (1 ms)
[----------] 2 tests from StockTests (2 ms total)

[----------] Global test environment tear-down
[==========] 4 tests from 2 test cases ran. (3 ms total)
[  PASSED  ] 4 tests.
                        

Command Line Parameters

Repeat test suite multiple times

./finprogtests --gtest_repeat=100
                        
Run specific tests (regex match)

./finprogtests --gtest_filter=StockRepositoryTests.*
                        

Why Mock?

## What to Eliminate Anything non-deterministic that can't be reliably controlled within the unit test * External data sources (e.g. files, databases) * Network connections (e.g. services) * External code dependencies (libraries) * Internal code dependencies * simpler test code * makes individual tests less brittle * some **downsides** to this
## Solution: Use Test Double ![Double](images/brad_double_small.jpg) * A **test double** is an object or function substituted for a "real" (production ready) object during testing. * Should appear exactly the same as a "real" production instance to its clients (collaborators). * Term originates from a notion of a _"stunt double"_ in films
## Types * **Stubs** return hard-coded values * **Spies** record the code's interaction with collaborators * times method called and passed arguments * **Mocks** return hard-coded values and verify interaction * cross between a stub and a spy
Mocks are the focus of this talk

Google Mock

## Coin Flipper ![](images/coin-flip.jpg) * A simple game to flip a coin * `CoinFlipper` class implements the game * It interacts with a random number generator * We can change a number generator at runtime * Goal is to **test** `CoinFlipper`

Interfaces


class Rng {
  public:
    virtual ~Rng();
    virtual double generate(double min, double max) = 0;
};
                        

class CoinFlipper {
  private:
    Rng* d_rng; // held, not owned

  public:
    enum Result {
        HEADS = 0,
        TAILS = 1
    };

    explicit CoinFlipper(Rng* rng);
    Result flipCoin() const;
};
                        

Implementation


CoinFlipper::CoinFlipper(Rng* rng) : d_rng(rng)
{
    BSLS_ASSERT(d_rng);
}

CoinFlipper::Result CoinFlipper::flipCoin() const
{
    const double val = d_rng->generate(0.0, 1.0);
    BSLS_ASSERT(0.0 <= val && val <= 1.0);

    return (val < 0.5) ? HEADS : TAILS;
}
                        

Playing the Game


... generator; // Construct a particular generator

// Create a game
CoinFlipper game(&generator);

// Start playing
CoinFlipper::Result flip = game.flip();
                        
flip is either HEADS or TAILS
  • One collaborator -- Rng
  • Real RNG is unpredictable
  • We want to test CoinFlipper produces both results
    • we also want these tests to be repeatable
    • without relying on an external environment
  • Have to mock Rng

Google Mock to the Rescue!

  • create mock classes using simple macros
  • rich set of matchers and actions
  • easily extensible by users to provide even richer features

Defining Mock Collaborators


MOCK_METHOD[n](methodName, returnType(arg1Type, ..., argNType));
MOCK_CONST_METHOD[n](methodName, returnType(arg1Type, ..., argNType));
                        

class MockRng : public Rng {
  public:
    MOCK_METHOD2(generate, double(double, double));
};
                        
## Defining Mock Collaborators * `~Rng()` must be `virtual` * If `generate()` is defined in a superclass, then it must be declared `virtual` in that superclass * In other words, **you can't mock non-virtual methods**

Setting Expectations


// test fails in `generate()` is not called exactly once with
// `min = 0.0` and `max = 1.0`
MockRng rng;
EXPECT_CALL(rng, generate(DoubleEq(0.0), DoubleEq(1.0))
    .Times(Exactly(1))
    .WillOnce(Return(0.25));
                        

EXPECT_CALL(mockObject, method(arg1Matcher, ..., argNMatcher))
    .Times(cardinality)          // 0 or 1
    .InSequence(sequences)       // 0+
    .WillOnce(action)            // 0+
    .WillRepeatedly(action)      // 0 or 1
                        

Full Expectation Format


EXPECT_CALL(mockObject, method(arg1Matcher, ..., argNMatcher))
    .With(multiArgumentMatcher)  // 0 or 1
    .Times(cardinality)          // 0 or 1
    .InSequence(sequences)       // 0+
    .After(expectations)         // 0+
    .WillOnce(action)            // 0+
    .WillRepeatedly(action)      // 0 or 1
    .RetiresOnSaturation();      // 0 or 1
                        

Default Actions

Specify default actions a mock should take, without setting strict expectations on whether they should be called


// Test passes regardless of whether `rng.generate()` is called or not.
// here. This just sets the default action (return `min` argument back
// to caller)
MockRng rng;
ON_CALL(rng, generate(_, _))
    .WillByDefault(ReturnArg<0>());
                        

ON_CALL(mockObject, method(matchers))
    .With(multiArgumentMatcher)  // 0 or 1
    .WillByDefault(action);
                        
## `ON_CALL` or `EXPECT_CALL`? * `ON_CALL` defines *stubs* * `EXPECT_CALL` defines *mocks* * Tests using loads of mocks become tightly coupled with the component's implementation
### Rule of Thumb Use `ON_CALL` unless you actually want to test a method is called a specific number of times
## Structuring Mock Tests * `CoinFlipper` generates a value in the range `[0,1]` * `<0.5` means `HEADS`, `>=0.5` means `TAILS` * Let's write a test to ensure `HEADS` is returned when the generated value is `<0.5`

Test Flow


TEST(TestFixture, TestName) {
    // 1) Create mock objects (collaborators)

    // 2) Specify your expectations of them

    // 3) Construct object(s) under test, passing mocks

    // 4) Run code under test

    // 5) Check output (using Google Test or some other framework)

    // 6) Let gmock automatically check mock expectations were met at
    //    end of test
}
                        

Test Flow


TEST(CoinFlipper, ShouldReturnHeadsIfRandValueIsLessThanProbability) {
    // 1) Create mock objects (collaborators)
    MockRng rng;

    // 2) Specify your expectations of them
    EXPECT_CALL(rng, generate(DoubleEq(0.0), DoubleEq(1.0)))
        .Times(Exactly(1))
        .WillOnce(Return(0.25));

    // 3) Construct object(s) under test, passing mocks
    CoinFlipper coinFlipper(&rng);

    // 4) Run code under test
    CoinFlipper::Result result = coinFlipper.flipCoin();

    // 5) Check output (using Google Test or some other framework)
    EXPECT_EQ(CoinFlipper::HEADS, result);

    // 6) Let gmock automatically check mock expectations were met at end of test
}
                        

Parametrised Tests


TEST_P(CoinFlipper, CoinFlip) {
    const double randomVal = GetParam().first;
    const CoinFlipper::Result expectedResult = GetParam().second;

    MockRng rng;
    EXPECT_CALL(rng, generate(DoubleEq(0.0), DoubleEq(1.0)))
        .Times(Exactly(1))
        .WillOnce(Return(randomVal));

    CoinFlipper coinFlipper(&rng);
    CoinFlipper::Result result = coinFlipper.flipCoin();

    EXPECT_EQ(expectedResult, result);
}

INSTANTIATE_TEST_CASE_P(ValidRandomNumberGenerated, CoinFlipper,
                        Values(bsl::make_pair(0.0, CoinFlipper::HEADS),
                               bsl::make_pair(0.25, CoinFlipper::HEADS),
                               bsl::make_pair(0.49999, CoinFlipper::HEADS),
                               bsl::make_pair(0.5, CoinFlipper::TAILS),
                               bsl::make_pair(0.75, CoinFlipper::TAILS),
                               bsl::make_pair(1.0, CoinFlipper::TAILS)));
                        

Matchers

Matchers are functions used to match mock inputs to their expected values


// Matchers are used to set expectations on the values of mocked
// function arguments
EXPECT_CALL(printer, printVec(UnorderedElementsAre("foo", "bar")))
    .Times(Exactly(1));

// They can also be outside of call expectations, to match any kind
// of value
EXPECT_THAT(someVector, UnorderedElementsAre("foo", "bar"));
ASSERT_THAT("my name is Donald", HasSubstr("Donald"));
                        

Common Matchers

Google Mock provides lots of matchers out-of-the-box

Matcher Matches
_ matches anything
Eq(value) values using operator==()
DoubleEq(value) values using fuzzy 64-bit float equality
IsNull() null raw/smart pointers
StrCaseEq(string) string (case-insensitive)
HasSubstr(string) strings with given substring

Common Matchers

Google Mock provides lots of matchers out-of-the-box

Matcher Matches
Contains(elem) containers that contain elem at least once
UnorderedElementsAre(e0, e1, ...) containers that contain specified elements, ignoring order
Field(&class::field, matcher) objects with value for specified member variable
(e.g. obj.d_age)
Property(&class::property, matcher) objects with value for specified member function
(e.g. obj.age())

Nesting Matchers


// writeVec() should be called once, with a 2-element a vector where:
//   * one element ~= 0.53
//   * one element that's not 1.0
EXPECT_CALL(writer, writeVec(UnorderedElementsAre(DoubleEq(0.53),
                                                  Not(DoubleEq(1.0)))))
    .Times(Exactly(1)).WillOnce(Return(0));

// writePerson() should be called once, with a person named Donald
// (case-insensitive) who is 102 years old
EXPECT_CALL(writer, savePersonToFile(
                       AllOf(
                             Property(&Person::name, StrCaseEq("donald")),
                             Property(&Person::age, 102)
                       ));
    .Times(Exactly(1)).WillOnce(Return(0));
                        

Defining Matchers


MATCHER(IsEven, "") { return (arg % 2) == 0; }

MATCHER_P(IsDivisibleBy, n, "") {
    return (arg % n) == 0;
}
                        

// all numbers in vector being written should be even
EXPECT_CALL(writer, writeVec(Each(IsEven))
    .Times(Exactly(1)).WillOnce(Return(0));

// all numbers in vector being written should be divisible by 3
EXPECT_CALL(writer, writeVec(Each(IsDivisibleBy(3))))
    .Times(Exactly(1)).WillOnce(Return(0));
                        

Actions

Actions specify what a mock method does when called


EXPECT_CALL(writer, writeVec(_))
    .Times(Exactly(1)).WillOnce(
        Return(1) // action
    );
                        

Common Actions

Action
Return(value) Return specified value
SetArgPointee<N>() Set value of Nth argument passed to mocked method
(useful for out parameters)
Throw(exception) Throw specified exception
Invoke(f) Invoke function f with arguments passed to mocked method
DoAll(a1, a2, ..., aN) Perform multiple actions in sequence

Defining Actions


// return statement in actions causes mock method to return this value
ACTION(Sum) {
    return arg0 + arg1;
}

// parametrised action
ACTION_P(AppendName, name) {
    arg0.push_back(name);
}
                        

EXPECT_CALL(calculator, add(_, _))
    .Times(Exactly(1)).WillOnce(Sum());

EXPECT_CALL(customerDatabase, retrieveCustomerNames(_))
    .Times(Exactly(1)).WillOnce(DoAll(
        AppendName("Bob"),
        AppendName("Susie"),
        Return(0)             // retrieval was a success
    ));
                        
## Summary
## Test Doubles * Replace collaborators with **test doubles**, which: * removes dependencies on non-deterministic behaviour * reduces setup code in tests * Makes tests easier to write and maintain * Use **stubs** if it's awkward to use the real thing * Use **mocks** when you want to test the *communication* between components
## Google Test and Mock * **Google Test** is a C++ testing framework * **Google Mock** is a C++ framework for defining stubs/mocks * large set of matchers/actions * extensible * often used in conjunction with Google Test
## When to Use Google Mock * Google Mock is not a panacea * Shouldn't be used to replace all collaborators * Awkward to use generated mocks in certain situations: * complex data structures * commonly used interfaces * Using hand-crafted test implementations is a better choice in some circumstances * Google Mock **suitable for most cases**
## Useful Resources * Detailed documentation on GitHub: * [Google Test](https://github.com/google/googletest/tree/master/googletest/docs) * [Google Mock](https://github.com/google/googletest/tree/master/googletest/docs) * [Mocks Aren't Stubs](http://martinfowler.com/articles/mocksArentStubs.html) * *classical* vs. *mockist* testing * [Growing Object-Oriented Software, Guided by Tests](http://www.amazon.co.uk/Growing-Object-Oriented-Software-Guided-Signature/dp/0321503627)