Kompleksowa eksploracja Google Mock – cardinality, matchers i actions w praktyce
Google Mock (gMock) to kluczowy komponent frameworka GoogleTest, umożliwiający zaawansowane testowanie jednostkowe przy użyciu obiektów pozornych. Artykuł ten stanowi pogłębioną analizę trzech podstawowych koncepcji gMock – cardinality, matchers i actions – prezentując praktyczne wskazówki implementacyjne. Bazując na oficjalnej dokumentacji oraz przykładach z rzeczywistych wdrożeń, analizujemy funkcjonalności każdego z elementów, ich wzajemne zależności oraz niuanse ich zastosowania.
1. Wprowadzenie do podstaw gMock
gMock wspiera testowanie oparte na zachowaniu poprzez symulowanie zależności za pomocą obiektów mokowanych. Pozwala to programistom na:
- Definiowanie oczekiwań (np. liczba wywołań funkcji, wartości argumentów);
- Określanie zachowań (np. zwracane wartości, skutki uboczne);
- Walidację interakcji (np. sekwencje wywołań, liczba wywołań).
StrukturaEXPECT_CALLstanowi podstawową składnię konfigurowania moków, łącząc cardinality, matchers i actions w jednolitą deklarację. Przykład:EXPECT_CALL(mock_object, method(matchers)) .Times(cardinality) .WillOnce(action) .WillRepeatedly(action);Ten schemat umożliwia precyzyjną kontrolę zachowania obiektów pozornych, przekształcając testy jednostkowe w ekspresyjne specyfikacje działania systemu.
2. Cardinality – ilościowe określenie oczekiwań
Cardinality definiuje, jak często dana funkcja mokowana powinna być wywołana, co określamy za pomocą klauzuli Times(). gMock obsługuje zarówno jawne, jak i rozmyte cardinalities, dostosowując się do różnorodnych scenariuszy testowych.
2.1 Jawne cardinalities
- Times(n) – dokładna ilość wywołań;
EXPECT_CALL(turtle, GetX()).Times(3); // Funkcja musi być wywołana dokładnie 3 razy
- Times(0) – blokuje wywołania (równoważne z
Never()).
2.2 Przybliżone cardinalities
- AtLeast(n) – minimum n wywołań;
- AtMost(n) – maksimum n wywołań;
- Between(m, n) – liczba wywołań w przedziale od m do n (włącznie);
- AnyNumber() – brak ograniczeń liczby wywołań.
2.3 Zasady domyślnego cardinality
Jeżeli nie zastosujemy Times(), gMock domyślnie określa cardinality na podstawie klauzul akcji:
- brak działań: Times(1) (jedno wywołanie);
- tylko
WillOnce(): Times(n) (po jednym wywołaniu dla każdegoWillOnce); WillOnce()+WillRepeatedly(): Times(AtLeast(n)) (minimum n wywołań);
Przykład:
EXPECT_CALL(turtle, GetY())
.WillOnce(Return(100))
.WillOnce(Return(200))
.WillRepeatedly(Return(300));
gMock wywnioskuje AtLeast(2) wywołania: dwa pierwsze zwracają odpowiednio 100 i 200, kolejne – 300.
Typowy błąd: Nadpisanie Times() wyłącza domniemaną liczność. Jeżeli określimy Times(4) i podamy tylko dwa WillOnce(), ostatnie dwa wywołania wykonają domyślne działanie mokowanej metody.
3. Matchers – walidacja argumentów
Matchers pozwalają weryfikować argumenty przekazywane do mokowanych funkcji, umożliwiając precyzyjną kontrolę oczekiwanych wartości lub warunków.
3.1 Wbudowane matchers
- Matchers wartości – porównują argumenty do oczekiwanych wartości;
EXPECT_CALL(foo, Bar(Eq(5))); // Argument musi być równy 5
EXPECT_CALL(foo, Bar(Gt(10))); // Argument > 10
- Matchers kontenerów – porównują zawartość, strukturę lub elementy;
EXPECT_CALL(foo, Process(ElementsAre(1, Lt(0), _, 5))); // 4 argumenty: 1, liczba ujemna, dowolny, 5
3.2 Matchers złożone (Composite)
Kombinujemy matchers za pomocą operatorów logicznych:
- AllOf(m1, m2) – wszystkie warunki muszą być spełnione;
- AnyOf(m1, m2) – wystarczy spełnienie jednego warunku;
- Not(m) – neguje matcher.
Przykład:
using ::testing::AllOf;
using ::testing::Lt;
EXPECT_CALL(foo, Blah)
.With(AllOf(Args<0, 1>(Lt()), Args<1, 2>(Lt())));
Wymaga, aby argumenty były w relacji 0 < 1 < 2.
3.3 Matchers polimorficzne
- _ – pasuje do dowolnego argumentu (z zachowaniem typów);
EXPECT_CALL(foo, Bar(_)); // Dowolny pojedynczy argument
- Własny predykat – własna funkcja warunkowa;
EXPECT_CALL(foo, Bar(Truly([](int x){ return x % 2 == 0; })));
4. Actions – definiowanie zachowania mocka
Actions definiują, co wykonuje mokowana funkcja, np. zwracanie wartości, wywołania zwrotne czy zmiany stanu.
4.1 Wbudowane actions
- Zwracanie wartości;
.WillOnce(Return(42))
.WillRepeatedly(Return(0));
- Efekty uboczne;
.WillOnce(Invoke([](){ WriteLog("Wywołano!"); }));
- Manipulacja argumentami;
using ::testing::SetArgPointee;
EXPECT_CALL(weather_station, wind(_, _))
.WillOnce(DoAll(SetArgPointee<0>(North), SetArgPointee<1>(0.5)));
4.2 Sekwencjonowanie actions
- WillOnce() – definiuje zachowanie dla kolejnego wywołania;
- WillRepeatedly() – definiuje zachowanie dla wszystkich kolejnych wywołań.
Przykład:
EXPECT_CALL(turtle, GetZ())
.WillOnce(Return(50))
.WillOnce(Return(60))
.WillRepeatedly(Return(70));
1. wywołanie: 50, drugie: 60, kolejne: 70.
4.3 Własne akcje (Custom actions)
- Makro ACTION() – pozwala na definiowanie własnych działań;
ACTION(Sum) { return arg0 + arg1; }
EXPECT_CALL(calc, Add).WillRepeatedly(Sum());
- Akcje parametryzowane;
ACTION_P(Add, n) { return arg0 + n; }
EXPECT_CALL(calc, Add).WillOnce(Add(5));
5. Przykłady praktyczne – integracja w testach
5.1 Mockowanie systemu paliwowego
class Car {
public:
virtual void addFuel(double amount) = 0;
};
class MockCar : public Car {
public:
MOCK_METHOD(void, addFuel, (double amount), (override));
};
TEST(CarTest, FuelAdditions) {
MockCar c;
EXPECT_CALL(c, addFuel(Gt(5.0)))
.Times(Between(1, 3))
.WillRepeatedly(Invoke([](double amt){ /* ... */ }));
}
- addFuel wywołane 1–3 razy,
- argument zawsze > 5.0,
- wykonywana własna logika przy każdym wywołaniu.
5.2 Testowanie callbacków
using ::testing::MockFunction;
using ::testing::_;
TEST(CallbackTest, StdFunction) {
MockFunction mock_callback;
auto callback = mock_callback.AsStdFunction();
EXPECT_CALL(mock_callback, Call(_))
.Times(2)
.WillOnce(Return(10))
.WillOnce(Return(20));
callback("test"); // Zwraca 10
callback("test"); // Zwraca 20
}
Wykorzystuje MockFunction do weryfikacji wywołań std::function.
6. Zaawansowane wzorce
6.1 Kolejność oczekiwań
- InSequence – wymusza sekwencyjność oczekiwań;
- After – ustala zależności między oczekiwaniami.
using ::testing::Expectation;
Expectation init = EXPECT_CALL(obj, Init());
EXPECT_CALL(obj, Execute())
.After(init);
6.2 Obsługa nasycenia (saturation)
- RetiresOnSaturation() – wyłącza oczekiwanie po osiągnięciu jego cardinality.
EXPECT_CALL(obj, Method(_))
.Times(AnyNumber())
.RetiresOnSaturation();
7. Podsumowanie
Cardinality, matchers i actions wspólnie umożliwiają tworzenie ekspresywnych, łatwych w utrzymaniu testów jednostkowych w gMock:
- Cardinality zapewnia, że funkcje są wywoływane prawidłową liczbę razy, eliminując niedopatrzenia lub nadmiary;
- Matchers umożliwiają precyzyjną weryfikację argumentów i ograniczanie fałszywych pozytywów;
- Actions oddzielają konfigurację testu od implementacji szczegółów.
Zalecane praktyki –
- Preferuj domyślne mechanizmy cardinality, gdy to możliwe;
- Kombinuj matchers za pomocą
AllOf/AnyOfw złożonej walidacji; - Stosuj makra
ACTION*do definiowania wielokrotnego, niestandardowego zachowania.
Opanowanie tych koncepcji zmienia testy jednostkowe w żywą dokumentację, zwiększa niezawodność kodu i przyspiesza rozwój oprogramowania. W przyszłości warto zagłębić się m.in. w mockowanie wielowątkowe czy integrację z frameworkami do testów właściwościowych.
Artykuł powstał na bazie dokumentacji gMock, praktycznych samouczków oraz doświadczenia z rzeczywistych projektów.
