Welcome back to our course on Test-Driven Development (TDD) in C++ using Google Test. In our previous lesson, we introduced the fundamentals of TDD and the Red-Green-Refactor workflow. Now, we will advance our TDD skills by focusing on generalizing solutions and enhancing the complexity of our testing scenarios.
As a brief reminder, TDD involves a repetitive cycle known as Red-Green-Refactor:
- Red: Write a failing test to clarify the new functionality you aim to implement.
- Green: Develop the smallest amount of code needed to make that test pass.
- Refactor: Clean up the code, enhancing its quality while maintaining its functionality and ensuring all tests remain passing.
In this lesson, we will expand upon the Sum
method, demonstrating how to generalize it while following these TDD principles.
Before we dive into coding, let's review our current setup. You are already familiar with the Sum
method from Math.hpp
, its implementation in Math.cpp
, and its corresponding test in MathTest.cpp
:
This existing setup serves as a foundation. Now, we'll focus on expanding your understanding by generalizing the approach using TDD principles. Understanding your starting point will help ensure future changes enhance our function without straying too far from the core logic.
To embrace the Red phase, let's introduce a new test case that will fail.
Update MathTest.cpp
to include more input scenarios:
By including a new scenario with new inputs, this step is intentionally set to fail to define our target goal clearly.
Upon running this test, you would receive output indicating failure, noting the discrepancy between expected and actual results. This confirms that the new functionality needs addressing.
Now, let's transition to the Green phase, where our goal is to ensure all tests pass, including our new one. We can update our Sum
method to use the generic response because it is the minimal solution.
Running the tests now should yield:
Success! The Green phase is complete, illustrating how effective writing minimal code to pass tests can be.
Finally, let's advance into the Refactor stage. The Sum
method is very simple, so there's nothing we can make better with the implementation. However, we do have a lot of duplication in our tests.
Let's introduce "parameterized testing" using Google Test's capabilities. A parameterized test is a single test that has several inputs and outputs. It makes it easy to add many test cases without duplicating too much test code.
Let's break down how this parameterized test works:
-
class SumTest : public ::testing::TestWithParam<std::tuple<int, int, int>> {};
- We create a test fixture that inherits fromTestWithParam
, which allows us to use parameters in our tests. Thestd::tuple<int, int, int>
defines our parameter type as a tuple containing three integers: two inputs and one expected output. -
TEST_P(SumTest, Sum_ShouldAddTwoNumbersCorrectly)
- The_P
suffix indicates this is a parameterized test. It will run once for each set of parameters we provide. -
Inside the test:
GetParam()
retrieves the current parameter tuplestd::get<0>
,std::get<1>
, andstd::get<2>
extract the individual elements from the tuple- We use these values as our test inputs and expected output
-
INSTANTIATE_TEST_SUITE_P
- This macro creates instances of our test for each parameter set:MathTests
- A prefix for the generated test namesSumTest
- The test fixture class to use::testing::Values(...)
- The set of parameters to use- Each
std::make_tuple(a, b, expected)
defines one test case
This refactoring doesn't change the functionality, but it makes our tests more maintainable. We can now add new test cases by simply adding new tuples to the Values
list, without duplicating the test logic.
In this lesson, we delved into using TDD to generalize solutions in C++ by:
- Reviewing and examining the initial state of the
Sum
method and its associated test cases. - Engaging in the Red phase by adding a new failing test to identify necessary functionality improvements.
- Advancing to the Green phase by implementing minimal code changes to pass the new test.
- Moving to the Refactor phase, where we utilized parameterized testing to reduce test duplication and improve maintainability.
Make sure to practice these techniques by trying to add more test cases to the Sum
method using this parameterized testing approach. As you become more comfortable with these concepts, you will find it easier to maintain and expand your code with confidence in its reliability.
