This is a full setup of parameterized tests (table-driven tests) with the GoogleTest framework. It includes using JSON test input from a file. If you’d like to skip the “story” and get to the code, you can download the CMake example project.
I assume you know what unit testing is and that tests are as important as the production code (or more important sometimes).
There are several ways to organize tests and their data (provided input and expected output). I’m testing the very simple sum function of a calculator because my focus here is on the tests, not on the tested code. Everything can be applied in more advanced contexts.
// calculator.hpp #ifndef CALCULATOR #define CALCULATOR namespace calculator { inline int sum(int a, int b) { return a + b; } } // namespace calculator #endif // CALCULATOR
Simple tests
The simplest test would call the function with some arguments and verify the result. I can create multiple similar tests for specific cases (eg. overflow). It’s the form I’m trying to use as often as possible. I like stupidly simple tests that are extremely easy to understand.
#include <gtest/gtest.h> #include "calculator.hpp" TEST(CalculatorSimpleTest, Sum) { const auto actual = calculator::sum(1, 2); const auto expected = 3; EXPECT_EQ(actual, expected); }
Table-driven tests
It’s a method I typically use when multiple simple tests (as above) would repeat. I can easily add multiple test cases in a configurable way; rather than thinking about the test code, I’m focusing on the test scenarios.
I always encourage scenario-based tests. What is more important than how. I construct the tests starting with the scenarios I want to cover (basic ones, edge cases), not thinking about lines of code to be covered. Although very important, the coverage should be a result, not a scope. The most important thing is for the source code to work as I promised to the user through the public interface.
If there are special scenarios that I want to be clearly stated by the tests, I add simple tests focused on particular cases besides the table-driven ones that cover the base cases.
The table is a container where each element is a test case.
#include <gtest/gtest.h> #include <vector> #include "calculator.hpp" TEST(CalculatorTableDrivenTest, Sum) { struct Test { int a; int b; int sum; }; const std::vector<Test> tests{{ {1, 2, 3}, {4, 5, 9}, }}; for (const auto& test : tests) { const auto actual = calculator::sum(test.a, test.b); const auto expected = test.sum; EXPECT_EQ(actual, expected); } }
In case of failure, I can provide details to identify faster what case failed. It’s more useful when I have many test cases. I can print the case number or I can add a description. Continue reading GoogleTest parameterized tests with JSON input