Created by Donald Whyte
/ @donald_whyte
Credit to Denis Cheklov for
his contributions
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=100
./finprogtests --gtest_filter=StockRepositoryTests.*
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
Rng
CoinFlipper
produces both results
Rng
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);
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 substring |
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() )
|
// 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
);
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 |
// 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
));