Given the following hypothetical legacy production code:
struct Foo
{
Bar bar;
bool baz()
{
return !bar.qux();
}
};
How do I mock bar.qux()
so that it returns true
in one test and false
in another test, without resorting to dependency injection?
I'm using Google Test framework, but want to avoid adding so many interfaces for dependency injections. Too many interfaces for the purpose of testability tends to make the production code overly complex.
We can probably do something like this:
#include <iostream>
#include <vector>
#include <deque>
// test/MockBarMonitorsAndControls.hpp
namespace test
{
bool const Bar_qux_default_output = true;
std::deque<bool> Bar_qux_outputs;
std::vector<int> Bar_qux_arg0_inputs;
}
// production/Bar.hpp
struct Bar
{
bool qux(int x);
};
// production/Bar.cpp
// bool Bar::qux(int x)
// {
// return static_cast<bool>(x & 2);
// }
// test/Bar.cpp
bool Bar::qux(int x)
{
test::Bar_qux_arg0_inputs.push_back(x);
if (!test::Bar_qux_outputs.empty())
{
auto result = test::Bar_qux_outputs.front();
test::Bar_qux_outputs.pop_front();
return result;
}
else
{
return test::Bar_qux_default_output;
}
}
// production/Foo.hpp
struct Foo
{
Bar bar;
int x = 9;
bool baz()
{
return !bar.qux(x);
}
void henry();
};
// test/BarTest.cpp
void TestBar1()
{
test::Bar_qux_outputs.clear();
test::Bar_qux_arg0_inputs.clear();
test::Bar_qux_outputs.push_back(false);
Foo foo;
bool pass = true;
auto result = foo.baz();
if (!result)
{
std::cout << "TEST1_ERROR: expected true result" << std::endl;
pass = false;
}
auto input = test::Bar_qux_arg0_inputs.front();
if (input != 9)
{
std::cout << "TEST1_ERROR: expected arg0 to be 9 but got " << input << std::endl;
pass = false;
}
if (pass)
{
std::cout << "TEST1 PASS" << std::endl;
}
else
{
std::cout << "TEST1 FAIL" << std::endl;
}
}
// test/BarTest.cpp
void TestBar2()
{
test::Bar_qux_outputs.clear();
test::Bar_qux_arg0_inputs.clear();
test::Bar_qux_outputs.push_back(true);
Foo foo;
foo.x = 21;
bool pass = true;
auto result = foo.baz();
if (result)
{
std::cout << "TEST2_ERROR: expected false result" << std::endl;
pass = false;
}
auto input = test::Bar_qux_arg0_inputs.front();
if (input != 21)
{
std::cout << "TEST2_ERROR: expected arg0 to be 21 but got " << input << std::endl;
pass = false;
}
if (pass)
{
std::cout << "TEST2 PASS" << std::endl;
}
else
{
std::cout << "TEST2 FAIL" << std::endl;
}
}
int main()
{
TestBar1();
TestBar2();
return 0;
}
Here's the sample output:
TEST1 PASS
TEST2 PASS