On Github DonaldWhyte / gmock-presentation
Created by Donald Whyte / @donald_whyte Credit to Denis Cheklov for his contributions
Cross-platform C++ testing framework by Google
Commonly used in conjunction with Google Mock
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:*");
// 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(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()); }
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_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()); }
. .. 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(); }
./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.
./finprogtests --gtest_repeat=100Run specific tests (regex match)
./finprogtests --gtest_filter=StockRepositoryTests.*
Anything non-deterministic that can't be reliably controlled within the unit test
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; };
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; }
... generator; // Construct a particular generator // Create a game CoinFlipper game(&generator); // Start playing CoinFlipper::Result flip = game.flip();flip is either HEADS or TAILS
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)); };
// 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
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
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);
Use ON_CALL unless you actually want to test a method is called a specific number of times
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(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 }
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 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"));
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 substringGoogle 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())// 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));
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 specify what a mock method does when called
EXPECT_CALL(writer, writeVec(_)) .Times(Exactly(1)).WillOnce( Return(1) // action );
// 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 ));