Close Menu
    Ciekawe

    Jak zabezpieczyć pendrive hasłem bez dodatkowych programów?

    2025-11-13

    Ile kosztuje prowadzenie jednoosobowej działalności gospodarczej? Przegląd opłat

    2025-11-10

    Acer czy Asus – który laptop wybrać? Porównanie i porady

    2025-11-05
    Facebook X (Twitter) Instagram
    CPP Polska
    Facebook X (Twitter) Instagram
    • Biznes

      Ile kosztuje prowadzenie jednoosobowej działalności gospodarczej? Przegląd opłat

      2025-11-10

      Jak wziąć samochód w leasing bez firmy? Poradnik dla osób fizycznych

      2025-10-29

      Jak założyć firmę jednoosobową krok po kroku – koszty, formalności i czas trwania

      2025-10-23

      Ile kosztuje stworzenie strony internetowej dla firmy? Cennik i porady

      2025-10-07

      Jak usunąć profil firmy z Google i Facebooka? Instrukcja krok po kroku

      2025-10-07
    • Technologie

      Jak zabezpieczyć pendrive hasłem bez dodatkowych programów?

      2025-11-13

      Acer czy Asus – który laptop wybrać? Porównanie i porady

      2025-11-05

      Jak przenieść okno na drugi monitor? Skróty i metody dla Windows i macOS

      2025-11-01

      Jak sprawdzić specyfikację laptopa? Pełna konfiguracja sprzętowa

      2025-10-26

      Co to jest VR? Wirtualna rzeczywistość i jej zastosowania

      2025-10-20
    • Programowanie

      Maszyna stanów oparta o std::variant

      2025-10-07

      Tablice w C++ od podstaw – deklaracja, inicjalizacja, iteracja i typowe pułapki

      2025-10-07

      std::deque w C++ – kiedy wybrać dwukierunkową kolejkę zamiast vectora

      2025-10-07

      itoa i std::to_chars – konwersja liczb na tekst bez narzutu wydajności

      2025-10-07

      strcpy vs strncpy vs std::string – bezpieczne kopiowanie łańcuchów w C++

      2025-10-07
    • Inne

      Jak prowadzić blog programistyczny i dzielić się wiedzą?

      2025-06-28
    CPP Polska
    Home»C++»Podstawy pracy z Google Mock – kurs krok po kroku
    C++

    Podstawy pracy z Google Mock – kurs krok po kroku

    Oskar KlimkiewiczBy Oskar KlimkiewiczUpdated:2025-06-28Brak komentarzy12 Mins Read
    Share Facebook Twitter LinkedIn Email Copy Link
    Follow Us
    RSS
    man in black suit jacket
    Share
    Facebook Twitter LinkedIn Email Copy Link

    Google Mock (gMock) stanowi nieodłączną część frameworka Google Test, oferując zaawansowane możliwości tworzenia atrap (mocków) w języku C++. Niniejszy przewodnik przedstawia fundamentalne zasady pracy z biblioteką, umożliwiając efektywne testowanie jednostkowe złożonych systemów poprzez symulację zachowania zależności. Głównymi obszarami zastosowania gMock są izolowanie testowanego kodu od zewnętrznych zależności, weryfikacja interakcji między obiektami oraz symulacja różnych scenariuszy błędów.

    Wprowadzenie do koncepcji atrap (mocków) w testowaniu

    Atrapy obiektów stanowią kluczowy element w testowaniu jednostkowym złożonych systemów, gdzie realizacja zasady „testuj tylko jedną jednostkę na raz” wymaga odizolowania testowanego komponentu od jego zależności. Podstawową funkcją mocków jest zastępowanie rzeczywistych obiektów ich kontrolowanymi odpowiednikami, które rejestrują wywołania metod i pozwalają na definiowanie oczekiwanych zachowań. W przeciwieństwie do obiektów typu fake, które stanowią działające uproszczone implementacje, mocki koncentrują się na weryfikacji interakcji między komponentami systemu. Google Mock oferuje deklaratywną składnię do definiowania klas mockowych, obsługę funkcji dowolnych typów, bogaty zestaw matcherów do walidacji argumentów oraz automatyczną weryfikację oczekiwań bez konieczności ręcznego rejestrowania wywołań.

    Framework gMock powstał pod wpływem inspiracji jMock i EasyMock, został jednak zaprojektowany z uwzględnieniem specyfiki języka C++, co przejawia się w pełnej obsłudze funkcji przeciążonych, metod szablonowych oraz zaawansowanych mechanizmów kontroli zachowań. Podstawową zaletą stosowania gMock jest możliwość tworzenia projektów o wyższej jakości architektonicznej – wymóg łatwego mockowania zależności naturalnie prowadzi do lepszego rozdzielenia odpowiedzialności między komponentami.

    Konfiguracja środowiska i instalacja biblioteki

    Integracja Google Mock z projektem wymaga poprawnej konfiguracji środowiska build. W przypadku użycia systemu CMake minimalna konfiguracja obejmuje dodanie katalogów nagłówkowych oraz połączenie z bibliotekami gtest i gmock:

    include_directories(
      ${CMAKE_CURRENT_SOURCE_DIR}/third_party/googletest/include
      ${CMAKE_CURRENT_SOURCE_DIR}/third_party/googlemock/include
    )
    target_link_libraries(moj_projekt gtest gmock gtest_main)
    

    W przypadku ręcznej kompilacji bez systemu budowania, konieczne jest dołączenie plików źródłowych: gmock-all.cc i gtest-all.cc z odpowiednimi ścieżkami do nagłówków. W środowisku Windows należy dodatkowo upewnić się, że projekt linkuje z biblioteką gmock_main, która zapewnia implementację funkcji main() niezbędnej do uruchomienia testów.

    Podstawowa struktura pliku testowego wymaga dołączenia niezbędnych nagłówków i inicjalizacji frameworka:

    #include <gmock/gmock.h>
    #include <gtest/gtest.h>
    
    TEST(TestSuite, TestName) {
        // Test logic
    }
    
    int main(int argc, char** argv) {
        testing::InitGoogleMock(&argc, argv);
        return RUN_ALL_TESTS();
    }
    

    Flagi wiersza poleceń umożliwiają dostosowanie zachowania frameworka: --gmock_catch_leaked_mocks=0 wyłącza raportowanie nieusuniętych mocków, podczas gdy --gmock_verbose=LEVEL kontroluje poziom szczegółowości logów (możliwe wartości: info, warning, error).

    Definiowanie klas mockujących

    Proces tworzenia klasy mockującej rozpoczyna się od zdefiniowania interfejsu, który ma być symulowany. Dla przykładu, rozważmy interfejs Turtle reprezentujący prosty system graficzny:

    class Turtle {
    public:
      virtual ~Turtle() = default;
      virtual void PenUp() = 0;
      virtual void PenDown() = 0;
      virtual void Forward(int distance) = 0;
      virtual void Turn(int degrees) = 0;
      virtual void GoTo(int x, int y) = 0;
      virtual int GetX() const = 0;
      virtual int GetY() const = 0;
    };
    

    Klasa mockująca MockTurtle dziedziczy po tym interfejsie i wykorzystuje makro MOCK_METHOD do deklaracji metod:

    #include <gmock/gmock.h>
    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));
    };
    

    Składnia makra MOCK_METHOD wymaga podania czterech elementów: typu zwracanego, nazwy metody, listy argumentów w nawiasach okrągłych oraz kwalifikatorów w nawiasach okrągłych. W przypadku metod const wymagane jest dodanie kwalifikatora const, a dla metod wirtualnych zaleca się dodanie override dla poprawy czytelności i zapobiegania błędom. Dla metod z różną liczbą argumentów istnieją warianty makra: MOCK_METHOD0 dla metod bez argumentów, MOCK_METHOD1 dla jednego argumentu itd., choć nowsze wersje gMock promują używanie pojedynczego makra z odpowiednią listą parametrów.

    Obsługa metod przeciążonych wymaga specjalnego podejścia – każda przeciążona wersja metody musi zostać zamockowana z odpowiednią sygnaturą. W przypadku klas szablonowych, proces mockowania przebiega identycznie jak dla zwykłych klas, z zastrzeżeniem, że mock również musi być szablonem:

    template <typename T>
    class StackInterface {
    public:
      virtual ~StackInterface() = default;
      virtual void Push(const T& x) = 0;
      virtual int GetSize() const = 0;
    };
    
    template <typename T>
    class MockStack : public StackInterface<T> {
    public:
      MOCK_METHOD(void, Push, (const T& x), (override));
      MOCK_METHOD(int, GetSize, (), (const, override));
    };
    

    Podstawy użycia mocków w testach

    Standardowy przepływ pracy z mockami w testach obejmuje trzy etapy: utworzenie obiektu mockującego, zdefiniowanie oczekiwań względem jego metod oraz wykonanie kodu testowanego, który korzysta z mocka. Rozważmy test metody DrawCircle klasy Painter, która wykorzystuje interfejs Turtle:

    TEST(PainterTest, CanDrawCircle) {
      MockTurtle turtle;
      EXPECT_CALL(turtle, PenDown())
        .Times(testing::AtLeast(1));
      Painter painter(&turtle);
      EXPECT_TRUE(painter.DrawCircle(0, 0, 10));
    }
    

    W tym przykładzie, makro EXPECT_CALL definiuje oczekiwanie, że metoda PenDown zostanie wywołana przynajmniej raz podczas wykonywania metody DrawCircle. Jeśli oczekiwanie nie zostanie spełnione, test zakończy się niepowodzeniem z czytelnym komunikatem błędu wskazującym przyczynę. Ważną cechą gMock jest automatyczna weryfikacja wszystkich oczekiwań w momencie niszczenia obiektu mockującego – jeśli jakiekolwiek oczekiwanie nie zostało spełnione, framework zgłosi błąd.

    Kluczową koncepcją w definiowaniu oczekiwań są kardynalności, które określają, ile razy metoda powinna zostać wywołana. Podstawowe kardynalności to:

    • Times(n) – dokładnie n wywołań;
    • AtLeast(n) – przynajmniej n wywołań;
    • AtMost(n) – maksymalnie n wywołań;
    • Between(m, n) – od m do n wywołań;
    • Exactly(n) lub po prostu n – alias dla Times(n).

    Konfiguracja zachowania metod

    Oprócz definiowania oczekiwań co do liczby wywołań, gMock pozwala na określenie działania metod mocka za pomocą akcji. Podstawowa akcja WillOnce określa zachowanie dla pojedynczego wywołania, podczas gdy WillRepeatedly definiuje zachowanie dla wszystkich kolejnych wywołań. Przykłady typowych akcji:

    EXPECT_CALL(turtle, GetX())
      .WillOnce(Return(100))    // Pierwsze wywołanie zwróci 100
      .WillOnce(Return(200))    // Drugie wywołanie zwróci 200
      .WillRepeatedly(Return(300)); // Kolejne zwrócą 300
    
    EXPECT_CALL(turtle, GoTo(_, _))
      .WillOnce(DoAll(
          SetArgReferee<0>(50),   // Ustawia pierwszy argument na 50
          SetArgReferee<1>(100),  // Ustawia drugi argument na 100
          Return()                // Zwraca void
      ));
    

    GMock oferuje bogaty zestaw wbudowanych akcji:

    • Return() – dla funkcji void;
    • Return(value) – zwraca wartość;
    • ReturnRef(variable) – zwraca referencję;
    • SetArgReferee<N>(value) – ustawia argument przekazany przez referencję;
    • Throw(exception) – rzuca wyjątek;
    • Invoke(functor) – wywołuje podany funktor.

    W przypadkach, gdy potrzebne jest bardziej złożone zachowanie, można wykorzystać akcję Invoke do wywołania niestandardowej funkcji:

    int ComputeComplexValue(int input) {
      return input * input + 2;
    }
    TEST(CalculatorTest, ComplexComputation) {
      MockCalculator calc;
      EXPECT_CALL(calc, Compute(_))
        .WillRepeatedly(Invoke(ComputeComplexValue));
      // Test logic
    }
    

    Walidacja argumentów przy użyciu matcherów

    Matchery pozwalają na precyzyjną weryfikację argumentów przekazywanych do metod mockowanych obiektów. Podstawowe matchery porównawcze:

    using ::testing::Eq;
    using ::testing::Ge;
    using ::testing::Lt;
    using ::testing::Ne;
    using ::testing::MatchesRegex;
    
    EXPECT_CALL(turtle, GoTo(Eq(50), Ge(100))); // x == 50, y >= 100
    EXPECT_CALL(turtle, Forward(Lt(10)));       // distance < 10
    EXPECT_CALL(turtle, Describe(MatchesRegex("[A-Za-z]+"))); // Nazwa składa się z liter
    

    Matchery kompozytowe umożliwiają tworzenie złożonych warunków:

    using ::testing::AllOf;
    using ::testing::AnyOf;
    using ::testing::Not;
    
    // Argument większy od 0 i nie równy 15
    EXPECT_CALL(turtle, Forward(AllOf(Gt(0), Ne(15))));
    // Argument mniejszy lub równy 0 lub większy od 100
    EXPECT_CALL(turtle, Forward(AnyOf(Le(0), Gt(100))));
    

    W przypadku metod przeciążonych, matchery są niezbędne do rozróżnienia wersji:

    class NameGenerator {
    public:
      virtual std::string Generate(const std::string& first, const std::string& last) = 0;
      virtual std::string Generate(int type) = 0;
    };
    class MockNameGenerator : public NameGenerator {
    public:
      MOCK_METHOD(std::string, Generate, (const std::string&, const std::string&), (override));
      MOCK_METHOD(std::string, Generate, (int), (override));
    };
    TEST(NameGeneratorTest, OverloadedMethods) {
      MockNameGenerator generator;
      EXPECT_CALL(generator, Generate(_, _));      // Wersja z dwoma stringami
      EXPECT_CALL(generator, Generate(An<int>())); // Wersja z intem
    }
    

    Testowanie wyjątków i błędów

    Symulowanie sytuacji wyjątkowych jest kluczowym scenariuszem w testowaniu obsługi błędów. GMock umożliwia łatwe rzucanie wyjątków jako akcje:

    TEST(DatabaseTest, ConnectionFailure) {
      MockDatabase db;
      EXPECT_CALL(db, Connect(_))
        .WillOnce(Throw(DatabaseTimeoutException("Connection timed out")));
      EXPECT_THROW(DatabaseManager::Instance().ConnectTo(db), DatabaseTimeoutException);
    }
    

    W przypadku testowania kodu, który nie powinien rzucać wyjątków, można wykorzystać:

    TEST(FileProcessorTest, ProcessValidFile) {
      MockFileReader reader;
      EXPECT_CALL(reader, Read(_))
        .WillOnce(Return("valid content"));
      FileProcessor processor(&reader);
      EXPECT_NO_THROW(processor.Process("test.txt"));
    }
    

    Kombinacja matcherów i akcji pozwala na tworzenie złożonych scenariuszy błędów:

    TEST(PaymentProcessorTest, InvalidTransaction) {
      MockPaymentGateway gateway;
      EXPECT_CALL(gateway, ProcessPayment(AllOf(
          Field(&Payment::amount, Gt(0)),
          Field(&Payment::currency, Eq("USD"))
      )))
        .WillOnce(Return(PAYMENT_INVALID_CURRENCY))
        .WillOnce(Return(PAYMENT_INSUFFICIENT_FUNDS))
        .WillRepeatedly(Return(PAYMENT_SUCCESS));
      PaymentProcessor processor(&gateway);
      // Testy różnych scenariuszy
    }
    

    Zarządzanie cyklem życia obiektów mockujących

    Poprawne zarządzanie pamięcią dla obiektów mockujących jest kluczowe, ponieważ gMock przeprowadza weryfikację oczekiwań w destruktorach. Dla obiektów tworzonych na stosie weryfikacja następuje automatycznie:

    TEST(StackObjectTest) {
      MockService service; // Obiekt na stosie
      EXPECT_CALL(service, Initialize());
      System system(&service);
      system.Start();
    } // Automatyczna weryfikacja przy wyjściu z zakresu
    

    Dla obiektów alokowanych dynamicznie należy ręcznie zadbać o ich usunięcie:

    TEST(HeapObjectTest) {
      MockService* service = new MockService;
      EXPECT_CALL(*service, Shutdown());
      System system(service);
      system.Stop();
      delete service; // Jawne usunięcie
    }
    

    Włączenie checker'a pamięci (np. AddressSanitizer) jest zalecane przy używaniu dynamicznych mocków. GMock oferuje również inteligentne wskaźniki dostosowane do jego mechanizmów weryfikacji:

    #include <gmock/gmock.h>
    #include <memory>
    TEST(SmartPointerTest) {
      auto service = std::make_unique<MockService>();
      EXPECT_CALL(*service, Initialize());
      System system(std::move(service));
      system.Start();
    } // Automatyczna weryfikacja przy destrukcji unique_ptr
    

    Zaawansowane techniki i najlepsze praktyki

    W bardziej złożonych scenariuszach testowych, gMock oferuje zaawansowane mechanizmy kontroli interakcji. Sekwencjonowanie pozwala na wymuszenie kolejności wywołań metod:

    TEST(SequencingTest) {
      MockTurtle turtle;
      testing::Sequence s1, s2;
      EXPECT_CALL(turtle, PenDown()).InSequence(s1, s2);
      EXPECT_CALL(turtle, Forward(10)).InSequence(s1);
      EXPECT_CALL(turtle, Turn(90)).InSequence(s2);
      EXPECT_CALL(turtle, Forward(10)).InSequence(s1, s2);
      // Kod testowy musi wywołać metody w odpowiedniej kolejności
    }
    

    Opcje NiceMock, StrictMock i NaggyMock kontrolują reakcję frameworka na niezdefiniowane wywołania metod:

    TEST(MockBehaviorTest) {
      StrictMock<MockTurtle> strict; // Błąd przy każdym nieoczekiwanym wywołaniu
      NiceMock<MockTurtle> nice;     // Ciche ignorowanie nieoczekiwanych wywołań
      NaggyMock<MockTurtle> naggy;   // Ostrzeżenia dla nieoczekiwanych wywołań
      // StrictMock spowoduje błąd przy jakimkolwiek wywołaniu bez EXPECT_CALL
    }
    

    W przypadku mockowania metod prywatnych, konieczne jest złamanie enkapsulacji za pomocą klasy zaprzyjaźnionej:

    class MyClass {
    private:
      virtual void InternalMethod(); // Metoda prywatna do zamockowania
      FRIEND_TEST(MyClassTest, InternalMethodTest); // Zaprzyjaźniony test
    };
    class MockMyClass : public MyClass {
    public:
      MOCK_METHOD(void, InternalMethod, (), (override));
    };
    TEST(MyClassTest, InternalMethodTest) {
      MockMyClass mock;
      EXPECT_CALL(mock, InternalMethod());
      // Testowanie zachowania
    }
    

    Najlepsze praktyki pracy z gMock obejmują:

    • Unikanie nadmiernego mockowania – mockuj tylko zależności, nie cały system;
    • Stosowanie atrap dla prawdziwych zależności – bazy danych, usług sieciowych;
    • Preferowanie StrictMock podczas testowania interakcji między komponentami;
    • Używanie matcherów zamiast sztywnych wartości tam, gdzie to możliwe;
    • Izolowanie testów poprzez mockowanie powiązanych komponentów;
    • Unikanie logiki w mockach – mocki powinny być "głupie".

    Typowe problemy i rozwiązywanie błędów

    Błędy w konfiguracji mocków często manifestują się jako niepowodzenia testów z czytelnymi komunikatami. Dla przykładu, komunikat:

    
    path/to/test.cc:42: Failure
    Actual function call count doesn't match this expectation:
      Actually: called once
      Expected: called twice.
    

    wskazuje na niezgodność w liczbie wywołań metody. GMock dostarcza szczegółowych informacji o oczekiwaniach i faktycznych wywołaniach, co znacznie ułatwia diagnozę problemów.

    Częste problemy i ich rozwiązania:

    • Niespełnione oczekiwania – sprawdź kolejność wywołań i warunki matcherów;
    • Nadmiarowe wywołania – rozważ użycie StrictMock lub dodaj RetiresOnSaturation();
    • Wycieki pamięci – upewnij się, że obiekty dynamiczne są usuwane, włącz AddressSanitizer;
    • Metody niewirtualne – mockowanie metod niewirtualnych wymaga użycia szablonów (TEMPLATE_TEST_CASE).

    Przypadki użycia i przykładowe scenariusze

    Rozważmy system płatności online, gdzie klasa PaymentProcessor zależy od zewnętrznego PaymentGateway:

    class PaymentProcessor {
    public:
      PaymentProcessor(PaymentGateway& gateway) : gateway_(gateway) {}
      PaymentResult ProcessPayment(const Payment& payment) {
        if (!gateway_.Validate(payment)) {
          return PAYMENT_INVALID;
        }
        int status = gateway_.Submit(payment);
        return InterpretStatus(status);
      }
    private:
      PaymentGateway& gateway_;
    };
    

    Testując PaymentProcessor w izolacji, mockujemy PaymentGateway:

    class MockPaymentGateway : public PaymentGateway {
    public:
      MOCK_METHOD(bool, Validate, (const Payment&), (override));
      MOCK_METHOD(int, Submit, (const Payment&), (override));
    };
    TEST(PaymentProcessorTest, ValidPayment) {
      MockPaymentGateway gateway;
      PaymentProcessor processor(gateway);
      Payment valid_payment = CreateValidPayment();
      EXPECT_CALL(gateway, Validate(_))
        .WillOnce(Return(true));
      EXPECT_CALL(gateway, Submit(valid_payment))
        .WillOnce(Return(200));
      EXPECT_EQ(PAYMENT_SUCCESS, processor.ProcessPayment(valid_payment));
    }
    TEST(PaymentProcessorTest, InvalidPayment) {
      MockPaymentGateway gateway;
      PaymentProcessor processor(gateway);
      Payment invalid_payment = CreateInvalidPayment();
      EXPECT_CALL(gateway, Validate(_))
        .WillOnce(Return(false));
      EXPECT_CALL(gateway, Submit(_))
        .Times(0);
      EXPECT_EQ(PAYMENT_INVALID, processor.ProcessPayment(invalid_payment));
    }
    

    Integracja z frameworkami testowymi

    Google Mock bezproblemowo integruje się z Google Test, tworząc kompleksowe środowisko do testowania. Przykład testu z wykorzystaniem fixtures:

    class DatabaseTest : public testing::Test {
    protected:
      void SetUp() override {
        mock_db_ = new MockDatabase();
        system_ = std::make_unique<DatabaseSystem>(mock_db_);
      }
      void TearDown() override {
        delete mock_db_;
      }
      MockDatabase* mock_db_;
      std::unique_ptr<DatabaseSystem> system_;
    };
    TEST_F(DatabaseTest, SuccessfulConnection) {
      EXPECT_CALL(*mock_db_, Connect("valid_credentials"))
        .WillOnce(Return(SUCCESS));
      EXPECT_TRUE(system_->Initialize("valid_credentials"));
    }
    TEST_F(DatabaseTest, FailedConnection) {
      EXPECT_CALL(*mock_db_, Connect("invalid_credentials"))
        .WillOnce(Throw(DatabaseException("Invalid credentials")));
      EXPECT_THROW(system_->Initialize("invalid_credentials"), DatabaseException);
    }
    

    Parametryzowane testy pozwalają na wykonywanie tych samych testów z różnymi danymi wejściowymi:

    class PaymentTest : public testing::TestWithParam<Payment> {};
    TEST_P(PaymentTest, Processing) {
      Payment payment = GetParam();
      MockPaymentGateway gateway;
      PaymentProcessor processor(gateway);
      EXPECT_CALL(gateway, Validate(_))
        .WillRepeatedly(Return(true));
      EXPECT_CALL(gateway, Submit(_))
        .WillRepeatedly(Return(200));
      EXPECT_EQ(PAYMENT_SUCCESS, processor.ProcessPayment(payment));
    }
    INSTANTIATE_TEST_SUITE_P(ValidPayments, PaymentTest, testing::Values(
        CreatePayment(100, "USD"),
        CreatePayment(50, "EUR"),
        CreatePayment(200, "GBP")
    ));
    

    Wsparcie dla technik TDD i developmentu sterowanego testami

    Google Mock doskonale wspiera praktyki Test-Driven Development. Rozważmy proces tworzenia systemu rezerwacji:

    1. Definiujemy interfejs ReservationSystem:
    class ReservationSystem {
    public:
      virtual bool MakeReservation(const Reservation& res) = 0;
      virtual bool CancelReservation(int id) = 0;
    };
    
    1. Piszemy testy dla komponentu korzystającego z systemu rezerwacji:
    TEST(ReservationManagerTest, SuccessfulReservation) {
      MockReservationSystem system;
      ReservationManager manager(&system);
      Reservation valid_res = CreateValidReservation();
      EXPECT_CALL(system, MakeReservation(valid_res))
        .WillOnce(Return(true));
      EXPECT_TRUE(manager.Book(valid_res));
    }
    
    1. Implementujemy ReservationManager naprzemiennie z testami;
    2. Dopiero po zapewnieniu pełnego pokrycia testami implementujemy rzeczywisty ReservationSystem;

    Takie podejście gwarantuje, że komponenty są od siebie niezależne i mogą być rozwijane równolegle. Mockowanie interfejsów na wczesnym etapie rozwoju systemu pozwala na wykrycie problemów z projektem API zanim zostanie zaimplementowana rzeczywista funkcjonalność.

    Rozszerzanie funkcjonalności Google Mock

    GMock oferuje mechanizmy rozszerzania poprzez definiowanie własnych akcji i matcherów. Niestandardowy matcher dla walidacji adresu email:

    MATCHER(IsValidEmail, "valid email address") {
      return std::regex_match(arg, std::regex("^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$"));
    }
    TEST(UserTest, EmailValidation) {
      MockUserValidator validator;
      EXPECT_CALL(validator, Validate(IsValidEmail()))
        .WillRepeatedly(Return(true));
      // Test logic
    }
    

    Niestandardowa akcja zapisująca argument do zewnętrznego kontenera:

    ACTION_P(SaveTo, container) {
      container->push_back(arg0);
    }
    TEST(DataCollectorTest, Collection) {
      MockSensor sensor;
      std::vector<int> readings;
      EXPECT_CALL(sensor, GetValue())
        .WillRepeatedly(SaveTo(&readings));
      DataCollector collector(&sensor);
      collector.Collect(100);
      EXPECT_EQ(100, readings.size());
    }
    

    Wydajność i optymalizacja w dużych testach

    Dla dużych projektów z rozbudowanymi suitami testowymi, wydajność mocków staje się istotnym czynnikiem. Wskazówki optymalizacyjne:

    1. Minimalizacja liczby mocków – stosuj mocki tylko tam, gdzie to konieczne;
    2. Prekompilacja nagłówków – użyj prekompilowanych nagłówków dla gmock/gtest;
    3. Płaskie hierarchie testów – unikaj głębokich hierarchii klas testowych;
    4. Mockowanie lekkich obiektów – uprość skomplikowane mocki;
    5. Selektywne ładowanie testów – uruchamiaj tylko niezbędne testy podczas rozwoju.

    W przypadku testów integracyjnych z wieloma zależnościami, zaleca się tworzenie hierarchii mocków:

    class SystemTest : public testing::Test {
    protected:
      void SetUp() override {
        db_mock_ = std::make_unique<MockDatabase>();
        network_mock_ = std::make_unique<MockNetworkService>();
        logger_mock_ = std::make_unique<MockLogger>();
        system_ = std::make_unique<ComplexSystem>(
          db_mock_.get(),
          network_mock_.get(),
          logger_mock_.get()
        );
      }
      std::unique_ptr<MockDatabase> db_mock_;
      std::unique_ptr<MockNetworkService> network_mock_;
      std::unique_ptr<MockLogger> logger_mock_;
      std::unique_ptr<ComplexSystem> system_;
    };
    

    Podsumowanie i dalsze kierunki rozwoju

    Google Mock stanowi potężne narzędzie w ekosystemie testowania C++, oferując kompleksowe rozwiązanie do tworzenia atrap obiektów. Podstawowe korzyści płynące z zastosowania tej biblioteki obejmują poprawę jakości kodu poprzez lepszą separację zależności, umożliwienie testowania scenariuszy trudnych do wywołania w rzeczywistym środowisku oraz przyspieszenie wykonania testów poprzez zastępowanie ciężkich zależności. Kluczowe elementy pracy z gMock to poprawne definiowanie klas mockujących z użyciem makra MOCK_METHOD, precyzyjne określanie oczekiwań za pomocą EXPECT_CALL z matcherami i kardynalnościami oraz konfiguracja zachowań metod poprzez akcje.

    Dla zaawansowanych użytkowników warto eksplorować takie funkcje jak mockowanie funkcji wolnych, symulowanie opóźnień w odpowiedziach oraz integracja z narzędziami do pokrycia kodu. W przypadku pracy w środowisku TDD, gMock staje się nieodzownym elementem procesu projektowania interfejsów i rozwoju systemu. Biblioteka stale ewoluuje, dodając wsparcie dla najnowszych standardów C++ oraz usprawniając diagnostykę błędów. Dalsze pogłębianie wiedzy warto oprzeć o oficjalną dokumentację Google Mock, szczególnie "gMock Cookbook" oraz "gMock Cheat Sheet", które dostarczają szczegółowych przykładów i wzorców użycia dla skomplikowanych przypadków.

    Polecane:

    • Google Mock: cardinality, matcher, action w praktyce
    • Google Mock – definiowanie zachowań i oczekiwań
    • Wyszukiwanie testów w Google Test – metody i narzędzia
    • Praktyczne użycie std::optional w nowoczesnym C++
    • Dlaczego unikamy wielodziedziczenia w C++
    Share. Facebook Twitter LinkedIn Email Copy Link
    Oskar Klimkiewicz
    • Website

    Inżynier oprogramowania specjalizujący się w C++, absolwent Wydziału Elektroniki i Technik Informacyjnych Politechniki Warszawskiej. Od ponad 8 lat projektuje i rozwija systemy o wysokiej dostępności, głównie dla branży fintech i IoT. PS. Zdjęcie wyretuszowane przez AI :)

    Podobne artykuły

    Maszyna stanów oparta o std::variant

    8 Mins Read

    Tablice w C++ od podstaw – deklaracja, inicjalizacja, iteracja i typowe pułapki

    4 Mins Read

    std::deque w C++ – kiedy wybrać dwukierunkową kolejkę zamiast vectora

    4 Mins Read
    Leave A Reply Cancel Reply

    Oglądaj, słuchaj, ćwicz - zdobywaj nowe umiejętności online
    Nie przegap

    Jak zabezpieczyć pendrive hasłem bez dodatkowych programów?

    Oskar Klimkiewicz5 Mins Read

    Zabezpieczenie danych na przenośnych nośnikach USB jest kluczowe we współczesnym środowisku cyfrowym, gdzie zagrożenia cybernetyczne…

    Ile kosztuje prowadzenie jednoosobowej działalności gospodarczej? Przegląd opłat

    2025-11-10

    Acer czy Asus – który laptop wybrać? Porównanie i porady

    2025-11-05

    Jak przenieść okno na drugi monitor? Skróty i metody dla Windows i macOS

    2025-11-01
    Social media
    • Facebook
    • Twitter
    • LinkedIn
    O nas
    O nas

    CPP Polska to serwis internetowy poświęcony technologii, programowaniu, IT, biznesowi i finansom. Znajdziesz tu porady, wskazówki i instrukcje dla wszystkich czytelników IT & Tech & Biz.

    Facebook X (Twitter) LinkedIn RSS
    Najnowsze

    Jak zabezpieczyć pendrive hasłem bez dodatkowych programów?

    2025-11-13

    Ile kosztuje prowadzenie jednoosobowej działalności gospodarczej? Przegląd opłat

    2025-11-10

    Acer czy Asus – który laptop wybrać? Porównanie i porady

    2025-11-05
    Popularne

    Skrajnie niepotrzebne, skrajne przypadki w C++

    2025-06-28

    Wyszukiwanie testów w Google Test – metody i narzędzia

    2025-06-28

    Czy C jest wolniejszy od C++? Zero-cost abstraction w praktyce

    2025-06-28
    © 2025 CPP Polska. Wszelkie prawa zastrzeżone.
    • Lista publikacji
    • Współpraca
    • Kontakt

    Type above and press Enter to search. Press Esc to cancel.