trainings/CreatingReliableSoftwareCpp/Presentation/testing_gmock.md

681 lines
No EOL
20 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

## GMOCK
* <!-- .element: class="fragment fade-in" --> Avoid depricated <code>MOCK_METHDn</code> and <code>MOCK_CONST_METHODn</code>
* <!-- .element: class="fragment fade-in" --> Use <code>MOCK_METHOD()</code>
* <!-- .element: class="fragment fade-in" --> Use <code>Times</code> when want to expect how many times function was call (by default it expect once)
* <!-- .element: class="fragment fade-in" --> Use <code>AtLeast</code> To expect <b>n</b> or more calls
* <!-- .element: class="fragment fade-in" --> Use <code>WillOnce</code> and <code>WillRepeatedly</code> to perform some actions
* <!-- .element: class="fragment fade-in" --> Use <code>SaveArg</code> to save argument provided to mock function
* <!-- .element: class="fragment fade-in" --> Use <code>InSequence</code> to perform sequenced actions
* <!-- .element: class="fragment fade-in" --> You can mock non-virtual function
* <!-- .element: class="fragment fade-in" --> Use <code>StrictMock</code> when you need to be sure about every call of this mock
* <!-- .element: class="fragment fade-in" --> Use <code>NiceMock</code> when you don't care about function called for this mock
* <!-- .element: class="fragment fade-in" --> Use <code>ON_CALL</code> and <code>WillByDefault</code> to perform some default action, when function will be called
* <!-- .element: class="fragment fade-in" --> Use <code>DoAll</code> to perform more then one action
* <!-- .element: class="fragment fade-in" --> Use <code>Invoke</code> to invoke funtion/lambda
<!-- .slide: style="font-size: 0.9em" -->
___
<!-- .slide: data-background="#ccc" -->
## MOCK_METHOD
MOCK_METHOD(return_type, name_of_function, arguments, parameters)
<!-- .element: class="fragment fade-in" -->
```C++
class MockTurtle : public Turtle {
public:
...
MOCK_METHOD(void, PenUp, (), (override));
MOCK_METHOD(void, PenDown, (), (override));
MOCK_METHOD(void, Forward, (int distance), (override));
MOCK_METHOD(void, Turn, (int degrees), (override));
MOCK_METHOD(void, GoTo, (int x, int y), (override));
MOCK_METHOD(int, GetX, (), (const, override));
MOCK_METHOD(int, GetY, (), (const, override));
};
```
<!-- .element: class="fragment fade-in" -->
* <!-- .element: class="fragment fade-in" --> const - Makes the mocked method a const method. Required if overriding a const method.
* <!-- .element: class="fragment fade-in" --> override - Marks the method with override. Recommended if overriding a virtual method.
* <!-- .element: class="fragment fade-in" --> noexcept - Marks the method with noexcept. Required if overriding a noexcept method.
* <!-- .element: class="fragment fade-in" --> Calltype(...) - Sets the call type for the method (e.g. to STDMETHODCALLTYPE), useful in Windows.
* <!-- .element: class="fragment fade-in" --> ref(...) - Marks the method with the reference qualification specified. Required if overriding a method that has reference qualifications. Eg ref(&) or ref(&&).
<!-- .slide: style="font-size: 0.9em" -->
___
<!-- .slide: data-background="#ccc" -->
## Test class
Let's use once agin `Foo` and `Bar` class
```C++
class Bar {
public:
virtual ~Bar() = default;
virtual bool doOtherStuff(const std::string& str, int*, const std::vector<int>& vec) const {
// do some stuff
return false;
}
};
class MockBar : public Bar {
public:
~MockBar() override = default;
MOCK_METHOD(bool, doOtherStuff, (const std::string&, int*, const std::vector<int>&), (const override));
};
class Foo {
public:
Foo(std::unique_ptr<Bar> bar)
: val_(std::make_unique<int>(42)),
bar_(std::move(bar)) {}
bool doSth(const std::string& str) {
return bar_->doOtherStuff(str, val_.get(), {1, 2, 3, 4});
}
private:
std::unique_ptr<int> val_;
std::unique_ptr<Bar> bar_;
};
```
<!-- .slide: style="font-size: 0.63em" -->
___
<!-- .slide: data-background="#ccc" -->
## Times
```C++
TEST(ExampleTest, ShouldTest) {
auto mockBar = std::make_unique<MockBar>();
auto* mockBarPtr = mockBar.get();
Foo foo(std::move(mockBar));
EXPECT_CALL(*mockBarPtr, doOtherStuff).Times(2);
foo.doSth("Sth1");
foo.doSth("Sth2");
}
```
<!-- .element: class="fragment fade-in" -->
<!-- .slide: style="font-size: 0.9em" -->
___
<!-- .slide: data-background="#ccc" -->
## AtLeast
```C++
TEST(ExampleTest, ShouldTest) {
auto mockBar = std::make_unique<MockBar>();
auto* mockBarPtr = mockBar.get();
Foo foo(std::move(mockBar));
EXPECT_CALL(*mockBarPtr, doOtherStuff).Times(testing::AtLeast(2));
foo.doSth("Sth1");
foo.doSth("Sth2");
}
```
<!-- .element: class="fragment fade-in" -->
___
<!-- .slide: data-background="#ccc" -->
## WillOnce and WillRepeatedly
```C++
TEST(ExampleTest, ShouldTest) {
auto mockBar = std::make_unique<MockBar>();
auto* mockBarPtr = mockBar.get();
Foo foo(std::move(mockBar));
// Dont do that. Next exceptaion will override first one!
// EXPECT_CALL(*mockBarPtr, doOtherStuff).WillOnce(Return(false));
// EXPECT_CALL(*mockBarPtr, doOtherStuff).WillOnce(Return(true));
// Correct one!
EXPECT_CALL(*mockBarPtr, doOtherStuff)
.WillOnce(Return(false))
.WillOnce(Return(true));
EXPECT_FALSE(foo.doSth("Sth1"));
EXPECT_TRUE(foo.doSth("Sth2"));
}
```
<!-- .element: class="fragment fade-in" -->
```C++
TEST(ExampleTest, ShouldTest) {
//...
EXPECT_CALL(*mockBarPtr, doOtherStuff)
.WillOnce(Return(false))
.WillOnce(Return(true))
.WillRepeatedly(Return(false));
EXPECT_FALSE(foo.doSth("Sth1"));
EXPECT_TRUE(foo.doSth("Sth2"));
EXPECT_FALSE(foo.doSth("Sth3"));
EXPECT_FALSE(foo.doSth("Sth4"));
}
```
<!-- .element: class="fragment fade-in" -->
<!-- .slide: style="font-size: 0.68em" -->
___
<!-- .slide: data-background="#ccc" -->
## Save arguments provided to functions (1)
We want to capture parameters provided to bar function.
```C++
class Bar {
public:
virtual ~Bar() = default;
virtual bool doOtherStuff(const std::string& str, int val, const std::vector<int>& vec) {
// do some stuff
return false;
}
};
class MockBar : public Bar {
public:
~MockBar() override = default;
MOCK_METHOD(bool, doOtherStuff, (const std::string&, int, const std::vector<int>&), (override));
};
class Foo {
public:
Foo(std::unique_ptr<Bar> bar)
: bar_(std::move(bar)) {}
bool doSth(const std::string& str) {
return bar_->doOtherStuff(str, 42, {1, 2, 3, 4});
}
private:
std::unique_ptr<Bar> bar_;
};
```
<!-- .element: class="fragment fade-in" -->
<!-- .slide: style="font-size: 0.65em" -->
___
<!-- .slide: data-background="#ccc" -->
## Save arguments provided to functions (2)
```C++
TEST(ExampleTest, ShouldTest) {
auto mockBar = std::make_unique<MockBar>();
auto* mockBarPtr = mockBar.get();
Foo foo(std::move(mockBar));
int val;
std::vector<int> vec;
// Also return true, instead of false like in original function
EXPECT_CALL(*mockBarPtr, doOtherStuff).
WillOnce(DoAll(SaveArg<1>(&val), SaveArg<2>(&vec), Return(true)));
EXPECT_TRUE(foo.doSth("Sth"));
EXPECT_EQ(val, 42);
const auto expected = std::vector<int>{1, 2, 3, 4};
EXPECT_EQ(vec, expected);
}
```
<!-- .element: class="fragment fade-in" -->
```
[----------] 1 test from ExampleTest
[ RUN ] ExampleTest.ShouldTest
[ OK ] ExampleTest.ShouldTest (0 ms)
[----------] 1 test from ExampleTest (0 ms total)
```
<!-- .element: class="fragment fade-in" -->
<!-- .slide: style="font-size: 0.78em" -->
___
<!-- .slide: data-background="#ccc" -->
## Save pointer provided to function
```C++
class Bar {
public:
virtual ~Bar() = default;
virtual bool doOtherStuff(const std::string& str, int* val, const std::vector<int>& vec) {
// do some stuff
return false;
}
};
```
<!-- .element: class="fragment fade-in" -->
```C++
TEST(ExampleTest, ShouldTest) {
auto mockBar = std::make_unique<MockBar>();
auto* mockBarPtr = mockBar.get();
Foo foo(std::move(mockBar));
int* val;
std::vector<int> vec;
// Capture by SaveArgPointee
EXPECT_CALL(*mockBarPtr, doOtherStuff).
WillOnce(DoAll(SaveArgPointee<1>(val), SaveArg<2>(&vec), Return(true)));
EXPECT_TRUE(foo.doSth("Sth"));
EXPECT_EQ(*val, 42);
const auto expected = std::vector<int>{1, 2, 3, 4};
EXPECT_EQ(vec, expected);
}
```
<!-- .element: class="fragment fade-in" -->
<!-- .slide: style="font-size: 0.7em" -->
___
<!-- .slide: data-background="#ccc" -->
## Capture non-copyable object
* <!-- .element: class="fragment fade-in" --> There is a problem to capture non-copyable objects because we can't copy them!
* <!-- .element: class="fragment fade-in" --> We need to make an ugly hack to capture this object -> move this object to our variable.
* <!-- .element: class="fragment fade-in" --> This will be problematic when this object needs to be used in a function, but it will work when we used in on mock function because the argument will not be used later.
```C++
// Bar
bool doSth(const std::string& str) {
return bar_->doOtherStuff(str, std::make_unique<int>(42), {1, 2, 3, 4});
}
TEST(ExampleTest, ShouldTest) {
auto mockBar = std::make_unique<MockBar>();
auto* mockBarPtr = mockBar.get();
Foo foo(std::move(mockBar));
std::unique_ptr<int> val;
EXPECT_CALL(*mockBarPtr, doOtherStuff).WillOnce(DoAll(MoveObject<1>(&val), Return(true)));
EXPECT_TRUE(foo.doSth("Sth"));
EXPECT_EQ(*val, 42);
}
```
<!-- .element: class="fragment fade-in" -->
* <!-- .element: class="fragment fade-in" --> <b>There is no such function in gtest, so we need to implement somehow <code>MoveObject</code></b>
<!-- .slide: style="font-size: 0.7em" -->
___
<!-- .slide: data-background="#ccc" -->
## Move object
```C++
ACTION_TEMPLATE(MoveObject,
HAS_1_TEMPLATE_PARAMS(int, k),
AND_1_VALUE_PARAMS(pointer)) {
using PtrType = std::remove_cv_t<std::remove_reference_t<decltype(std::get<k>(args))>>;
// This unique_ptr will be deleted after function end
// so we need to capture it
*pointer = std::move(const_cast<PtrType&>(std::get<k>(args)));
}
```
<!-- .element: class="fragment fade-in" -->
When we only need a raw pointer from `unique_ptr`
<!-- .element: class="fragment fade-in" -->
```C++
ACTION_TEMPLATE(ExtractPtr,
HAS_1_TEMPLATE_PARAMS(int, k),
AND_1_VALUE_PARAMS(pointer)) {
*pointer = std::get<k>(args).get();
}
```
<!-- .element: class="fragment fade-in" -->
But we will lost it, because `unique_ptr` will be deleted after exit form mock function!
<!-- .element: class="fragment fade-in" -->
<!-- .slide: style="font-size: 0.8em" -->
<b>This is a very old way, the better one is to just lambda instead, I will show this later</b>
<!-- .element: class="fragment fade-in" -->
___
<!-- .slide: data-background="#ccc" -->
## InSequence (1)
```C++
TEST(ExampleTest, ShouldTest) {
auto mockBar = std::make_unique<MockBar>();
auto* mockBarPtr = mockBar.get();
Foo foo(std::move(mockBar));
testing::Sequence seq;
// Four expectations in sequence. There is no override like before
for (int i = 0; i < 4; ++i) {
EXPECT_CALL(*mockBarPtr, doOtherStuff)
.InSequence(seq)
.WillOnce(Return(i % 2));
}
EXPECT_FALSE(foo.doSth("Sth1"));
EXPECT_TRUE(foo.doSth("Sth2"));
EXPECT_FALSE(foo.doSth("Sth3"));
EXPECT_TRUE(foo.doSth("Sth4"));
}
```
<!-- .element: class="fragment fade-in" -->
<!-- .slide: style="font-size: 0.9em" -->
___
<!-- .slide: data-background="#ccc" -->
## InSequence (2)
This has the same behaviour like previous example
<!-- .element: class="fragment fade-in" -->
```C++
TEST(ExampleTest, ShouldTest) {
auto mockBar = std::make_unique<MockBar>();
auto* mockBarPtr = mockBar.get();
Foo foo(std::move(mockBar));
{
testing::InSequence seq;
// Four expectations in sequence. There is no override like before
for (int i = 0; i < 4; ++i) {
EXPECT_CALL(*mockBarPtr, doOtherStuff).WillOnce(Return(i % 2));
}
}
EXPECT_FALSE(foo.doSth("Sth1"));
EXPECT_TRUE(foo.doSth("Sth2"));
EXPECT_FALSE(foo.doSth("Sth3"));
EXPECT_TRUE(foo.doSth("Sth4"));
}
```
<!-- .element: class="fragment fade-in" -->
<!-- .slide: style="font-size: 0.9em" -->
___
<!-- .slide: data-background="#ccc" -->
## InSequence - branch out
```C++
testing::Sequence s1, s2;
EXPECT_CALL(foo, Method1())
.InSequence(s1, s2);
EXPECT_CALL(bar, Method2())
.InSequence(s1);
EXPECT_CALL(bar, Method3())
.InSequence(s2);
EXPECT_CALL(foo, Method4())
.InSequence(s2);
```
<!-- .element: class="fragment fade-in" -->
```C++
+---> Method2 (seq1)
(seq1 seq2) |
Method1----|
|
+---> Method3 ---> Method4 (seq2)
```
<!-- .element: class="fragment fade-in" -->
<!-- .slide: style="font-size: 0.9em" -->
___
<!-- .slide: data-background="#ccc" -->
## mock non-virtual function (1)
We have the following class
<!-- .element: class="fragment fade-in" -->
```C++
struct Packet {};
class ConcretePacketStream {
public:
void AppendPacket(Packet* new_packet) {}
const Packet* GetPacket(size_t packet_number) const {}
size_t NumberOfPackets() const {}
};
```
<!-- .element: class="fragment fade-in" -->
<!-- .slide: style="font-size: 0.9em" -->
___
<!-- .slide: data-background="#ccc" -->
## mock non-virtual function (2)
We can create mock and instead of dynamic polymorphism use static polymorphism (templates)
<!-- .element: class="fragment fade-in" -->
```C++
// A mock packet stream class. It inherits from no other, but defines
// GetPacket() and NumberOfPackets(). No need to add all functions like: AppendPacket
class MockPacketStream {
public:
// Do not add override!
MOCK_METHOD(const Packet*, GetPacket, (size_t packet_number), (const));
MOCK_METHOD(size_t, NumberOfPackets, (), (const));
};
// Now we can use static polymorphism to insert Mock class in a test,
// and real class in production code
template <class PacketStream>
class PacketReader {
public:
const Packet* ReadPackets(PacketStream* stream, size_t packet_num) {
return stream->GetPacket(packet_num);
}
};
```
<!-- .element: class="fragment fade-in" -->
<!-- .slide: style="font-size: 0.8em" -->
___
<!-- .slide: data-background="#ccc" -->
## mock non-virtual function (3)
Now in test we can use mock class
<!-- .element: class="fragment fade-in" -->
```C++
TEST(TestNonVirtualMethod, ShouldTest) {
MockPacketStream mock_stream;
EXPECT_CALL(mock_stream, GetPacket).WillOnce(Return(nullptr));
PacketReader<MockPacketStream> reader;
reader.ReadPackets(&mock_stream, 12);
}
```
<!-- .element: class="fragment fade-in" -->
And in production code the real one
<!-- .element: class="fragment fade-in" -->
```C++
ConcretePacketStream stream;
PacketReader<ConcretePacketStream> reader;
auto* packet = reader.ReadPackets(&stream, 12);
```
<!-- .element: class="fragment fade-in" -->
<!-- .element: class="fragment fade-in" -->
<!-- .slide: style="font-size: 0.9em" -->
___
<!-- .slide: data-background="#ccc" -->
## StrictMock
The usage of `StrictMock` is similar to normal mock, except that it makes all uninteresting calls failures:
```C++
class MockFoo : public Foo {
//...
MOCK_METHOD(void, Fun1, (), (override));
MOCK_METHOD(void, Fun2, (), (override));
};
TEST(SimpleTest, TestSth) {
StrictMock<MockFoo> mock_foo;
// When Fun2 will be also called it cause test failed
EXPECT_CALL(mock_foo, Fun1());
}
```
<!-- .element: class="fragment fade-in" -->
<!-- .slide: style="font-size: 0.9em" -->
___
<!-- .slide: data-background="#ccc" -->
## NiceMock
* <!-- .element: class="fragment fade-in" --> The usage of `StrictMock` is similar to normal mock, except that it reject uninteresting calls (we will not see them while run test)
* <!-- .element: class="fragment fade-in" --> NiceMock and StrictMock only affects uninteresting calls (calls of methods with no expectations); they do not affect unexpected calls (calls of methods with expectations, but they dont match)
```C++
class MockFoo : public Foo {
//...
MOCK_METHOD(void, Fun1, (), (override));
MOCK_METHOD(void, Fun2, (), (override));
};
TEST(SimpleTest, TestSth) {
NiceMock<MockFoo> mock_foo;
// When Fun2 will be also called it will be ignored
EXPECT_CALL(mock_foo, Fun1());
}
```
<!-- .element: class="fragment fade-in" -->
<!-- .slide: style="font-size: 0.9em" -->
___
<!-- .slide: data-background="#ccc" -->
## ON_CALL and WillByDefault
We can do whatever we want when some mock method will be called. We can even save all types of arguments, or move objects to test them (call later).
<!-- .element: class="fragment fade-in" -->
When a function takes for instance non-copyable callback, and we need to capture it to continue testing, we can move it inside lambda! This is much better than the tricky template shown before.
<!-- .element: class="fragment fade-in" -->
```C++
TEST(ExampleTest, ShouldTest) {
auto mockBar = std::make_unique<MockBar>();
auto* mockBarPtr = mockBar.get();
Foo foo(std::move(mockBar));
// Nice way to capture all types of argument
int* ptr;
ON_CALL(*mockBarPtr, doOtherStuff)
.WillByDefault([&ptr](const std::string&, int* new_ptr, const std::vector<int>&) {
ptr = new_ptr;
return true;
});
EXPECT_CALL(*mockBarPtr, doOtherStuff);
EXPECT_TRUE(foo.doSth("Sth1"));
EXPECT_EQ(*ptr, 42);
}
```
<!-- .element: class="fragment fade-in" -->
<!-- .slide: style="font-size: 0.8em" -->
___
<!-- .slide: data-background="#ccc" -->
## DoAll
If we need to combine a few actions, we can do this inside `doAll(...)`
<!-- .element: class="fragment fade-in" -->
```C++
TEST(ExampleTest, ShouldTest) {
auto mockBar = std::make_unique<MockBar>();
auto* mockBarPtr = mockBar.get();
Foo foo(std::move(mockBar));
std::string str;
int* ptr;
EXPECT_CALL(*mockBarPtr, doOtherStuff)
.WillOnce(DoAll(
SaveArg<0>(&str),
SetArgPointee<1>(80), // Replace pointer value
SaveArgPointee<1>(ptr), // Capture value of arg1
Return(true)));
EXPECT_TRUE(foo.doSth("Sth1"));
EXPECT_EQ(str, "Sth1");
EXPECT_EQ(*ptr, 80);
}
```
<!-- .element: class="fragment fade-in" -->
<!-- .slide: style="font-size: 0.9em" -->
___
<!-- .slide: data-background="#ccc" -->
## Invoke (1)
Let's change a little `MockBar`
<!-- .element: class="fragment fade-in" -->
```C++
class MockBar : public Bar {
public:
~MockBar() override = default;
MOCK_METHOD(bool, doOtherStuff, (const std::string&, int*, std::function<void(int)>), (const override));
};
```
<!-- .element: class="fragment fade-in" -->
Now we want to run callback. We can do this without capture it!
<!-- .element: class="fragment fade-in" -->
```C++
EXPECT_CALL(*mockBarPtr, doOtherStuff)
.WillOnce(DoAll(
SaveArg<0>(&str),
SetArgPointee<1>(80), // Replace pointer value
SaveArgPointee<1>(ptr), // Capture value of arg1
InvokeArgument<2>(500), // Invoke callback
Return(true)));
```
<!-- .element: class="fragment fade-in" -->
<!-- .slide: style="font-size: 0.9em" -->
___
<!-- .slide: data-background="#ccc" -->
## Invoke (2)
```C++
class Helper {
public:
void validateArguments(const std::string& str, int* ptr, std::function<void(int)> callback) {
ASSERT_EQ(str.size(), 4);
EXPECT_EQ(str, "Sth1");
ASSERT_TRUE(ptr);
EXPECT_EQ(*ptr, 42);
ASSERT_TRUE(callback);
callback(50); // We can invoke callback here
}
};
TEST(ExampleTest, ShouldTest) {
auto mockBar = std::make_unique<MockBar>();
auto* mockBarPtr = mockBar.get();
Foo foo(std::move(mockBar));
Helper helper;
EXPECT_CALL(*mockBarPtr, doOtherStuff)
.WillOnce(DoAll(
Invoke(&helper, &Helper::validateArguments),
Return(true)));
EXPECT_TRUE(foo.doSth("Sth1"));
}
```
<!-- .element: class="fragment fade-in" -->
<!-- .slide: style="font-size: 0.76em" -->
___
## Exercise 2
Open project **SHM** and write test for class `Store.cpp` which will test it's interface. You need to mock `Cargo` class and check if you get correct result based on what you mock. Write mock in `Presentation/exercises/SHM/tests/CargoMock.h` for `Cargo` class.
Go to `Presentation/exercises/SHM/build/test` and run test using command `./SHM_Tests`.