Introduction and Context Setting

Welcome to the second lesson in our exploration of Test-Driven Development (TDD) using C++ with Google Test and Google Mock. In the previous lesson, we covered how to utilize dummies to isolate dependencies. In this lesson, we will focus on another type of test double — Stubs.

By the end of this lesson, you will understand what stubs are and how to implement them in your tests, specifically for isolating dependencies like external services within the C++ testing ecosystem.

Understanding Stubs in Testing

In testing, test doubles help us isolate parts of our application. We've previously discussed dummies. Now, we will explore stubs, a more useful type of test double. Stubs provide predefined answers to method calls during testing. Unlike other test doubles, stubs do not track their usage, making them simpler yet powerful for certain scenarios.

Stubs are particularly useful when testing functions that rely on external services or complex dependencies. By simulating function outputs, stubs make tests faster and more predictable. Keep in mind that stubs focus on ensuring your application's logic functions as expected without verifying the correctness of external dependencies.

In C++, Google Mock is a popular library used to create stubs. It allows us to set up return values that simulate how dependencies should behave in a controlled environment. This predictability isolates and tests your application's logic without relying on the behavior of external systems, which might be complex or introduce variability.

Example: Crafting a `WeatherAlertService` Using Stubs

To illustrate the concept of stubs, we will create a WeatherAlertService using stubs in a test-driven development process.

Getting Ready to Test

We will build a WeatherAlertService that fetches data from a WeatherService. This service will issue alerts based on specific conditions. The external data source is impractical for testing, so we'll use stubbed data for our tests instead.

Red: Writing the First Test

Create a new test file named WeatherAlertServiceTests.cpp with the following test setup:

In this test:

  • We create a stub WeatherServiceStub with methods SetWeather and GetCurrentWeather that provide predefined values, simulating the expected weather conditions.
  • We validate whether the WeatherAlertService correctly interprets the weather data and returns the appropriate alert, specifically checking if it generates a heat warning when the temperature exceeds 35 degrees.

Run this test and expect it to fail initially, as WeatherAlertService is not yet implemented to handle the specified conditions.

Green: Making the Test Pass

Implement the necessary classes to make our test pass. Create the following in your implementation:

Run the tests again. The objective is to pass the specific test scenario by implementing only the necessary logic without overengineering other cases.

Refactor: Introduce a Stub Using the Google Mock Library

In the initial implementation, we created a manual stub, WeatherServiceStub, to simulate weather data. Now, we will refactor this by leveraging the Google Mock framework to streamline the process and improve maintainability. Using Google Mock, we can create flexible and reusable stubs with minimal code.

The Google Mock library allows us to set up method return values dynamically. By substituting our hand-crafted stub with a Google Mock-based one, we achieve the same test outcomes but with enhanced clarity and simplicity. This approach decreases the potential for bugs and makes adjusting test parameters easier.

Here's how we refactor our WeatherAlertServiceTests:

  • Replace the WeatherServiceStub with a Mock of the IWeatherService.
  • Use the ON_CALL macro in Google Mock to define the stub behavior for the GetCurrentWeather method.

The refactored test class using the Google Mock library is shown below:

Notice the use of ON_CALL instead of EXPECT_CALL. This is a key distinction when creating stubs with Google Mock:

  • ON_CALL sets up a stub behavior without expectations about whether the method will be called, making it perfect for stubs that simply provide canned answers.
  • EXPECT_CALL would be used for mocks where you want to verify interactions with the dependency.

Since stubs are only concerned with providing predetermined outputs rather than verifying interactions, ON_CALL is the appropriate choice for implementing stub behavior.

This refactor using Google Mock simplifies test management and focuses the tests more on behavior rather than setup intricacies.

Summary and Preparation for Practice

In this lesson, we delved into the concept of stubs and how they serve as a practical method to isolate dependencies in tests. Here are the key takeaways:

  • Stubs offer a way to replace external dependencies by providing predefined return values, facilitating the testing of functionalities reliant on services beyond our immediate control.
  • Through the Red-Green-Refactor cycle, we emphasized writing tests that initially fail, implementing just enough logic to pass the tests, and refining the code consistently.

Prepare to apply these techniques in forthcoming exercises, where you'll explore different scenarios utilizing stubs. This will enhance your skills in employing test doubles and adhering to the TDD methodology, ultimately advancing your ability to create dependable, thoroughly tested code.

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