Introduction to Mocks

In our journey through isolating dependencies with test doubles, we've explored dummies, stubs, and spies. This lesson focuses on mocks, which are powerful test doubles capable of simulating external dependencies in software tests. You may have noticed in previous lessons how observing real implementations can be messy. Mocks can mimic the behavior of complex systems, allowing us to test code in isolation without relying on real and sometimes unpredictable systems like databases or web services.

Let's revisit the TDD workflow:

  • Red: Write a failing test.
  • Green: Write the minimum code to pass the test.
  • Refactor: Improve the code structure without changing its behavior.

We'll demonstrate these principles using mocks, helping you to effectively isolate and test your application logic in C++.

Why Use Mocks in TDD?

Mocks are indispensable in TDD because they allow you to test your code independently of the parts of the system you don't control. For instance, when writing tests for a PricingService, you don't want tests to fail because an external currency conversion API goes down or changes unexpectedly. Mocks provide a controlled environment where you can simulate various conditions and responses as well as validate the calls.

Mocks, unlike spies, fully simulate the dependencies rather than simply observing their behavior. A mock creates a controlled substitute for a dependency, so the actual code or functionality isn’t executed. For instance, if a function interacts with an external API, a mock can simulate different responses from that API without making a network request.

Mocking Fundamentals with Google Mock

Let's dive into mocking with Google Mock. We'll start with the basics: how to mock a class and its methods.

Consider the ExchangeRateService, which fetches exchange rates from an API. In testing the PricingService, we need to mock this service to ensure our tests don't rely on actual API responses.

Here's a simple way to mock with Google Mock:

#include <gtest/gtest.h>
#include <gmock/gmock.h>

class IExchangeRateService {
public:
    virtual ~IExchangeRateService() = default;
    virtual double GetRate(const std::string& fromCurrency, const std::string& toCurrency) = 0;
};

class MockExchangeRateService : public IExchangeRateService {
public:
    MOCK_METHOD(double, GetRate, (const std::string& fromCurrency, const std::string& toCurrency), (override));
};

class PricingService {
public:
    PricingService(IExchangeRateService* exchangeRateService)
        : exchangeRateService(exchangeRateService) {}

    double ConvertPrice(double amount, const std::string& fromCurrency, const std::string& toCurrency) {
        double rate = exchangeRateService->GetRate(fromCurrency, toCurrency);
        return amount * rate;
    }

private:
    IExchangeRateService* exchangeRateService;
};

TEST(PricingServiceTest, ConvertPrice_ShouldUseExchangeRate) {
    MockExchangeRateService mockExchangeRateService;
    PricingService pricingService(&mockExchangeRateService);

    EXPECT_CALL(mockExchangeRateService, GetRate("USD", "EUR"))
        .WillOnce(testing::Return(1.5));

    double result = pricingService.ConvertPrice(100, "USD", "EUR");
    EXPECT_EQ(150.0, result);
}

In this setup:

  • We define a MockExchangeRateService derived from IExchangeRateService using Google Mock. This mock object allows control over returned values and behavior without implementing the actual logic.
  • EXPECT_CALL() is used to simulate a method that returns a predefined value when called, similar to how setup is used in Moq.
Sign up
Join the 1M+ learners on CodeSignal
Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal