Introduction
Automated software tests are becoming more and more popular. Thanks to the idea of Test Driven Development (TDD), unit tests are used more and more often.
To implement unit tests, first we have to define what is a unit? In object oriented environment, usually a class is encountered as a unit. So in case of unit testing, we are testing the behavior of a class.
One way of thinking here is that only the public
interfaces need to be tested, since the private
ones are only used by the public
functions of the class, so if someone is using our class from outside, she/he only uses the public
interfaces and doesn’t have knowledge about the private
ones. This is also true.
On the other hand, one important KPI is the code coverage and to reach 100% code coverage, the easiest way is to test the private
functions separately. In some cases, the private
functions are really complex and it is difficult to test all the corner cases through the public
functions.
So there is a need for testing private
functions. But it is not so trivial to do it, since private
functions are not callable from outside of the class.
I collected some possible solutions for testing them. The solutions are dedicated to C++, but most of them can also be used with other object oriented programming languages.
Possible Solutions
Friend Class
In C++, a friend
class of a class can also see its private
attributes and functions. So make your test fixture class friend
to the tested class and you can unit test its private
functions.
However, it has the disadvantage that you have to modify the code of your tested class (by the line adding the friend
class). So this solution is not too elegant.
#include
class UnitTestClass;
class ToBeTested
{
private:
int Calculate() { return 0; }
friend UnitTestClass;
};
class UnitTestClass
{
public:
void RunTest()
{
ToBeTested object_under_test;
if (object_under_test.Calculate() == 0)
{
std::cout << "Test passed" << std::endl;
}
else
{
std::cout << "Test failed" << std::endl;;
}
}
};
int main()
{
UnitTestClass unit_tester;
unit_tester.RunTest();
return 0;
}
Define private public
In C++, preprocessor directives give you a lot of chances to cheat. You can just put such a code into your test file:
#define private public
#include
#undef private
Ugly, right? But it works...
Make It protected and inherit
You can change your private
functions to protected
, so that they have the same behavior until you are not inheriting from your class. And now, you can inherit your test fixture class from your class under test, so that you can reach all its protected
functions.
The dark side of this solution is that you are changing your production code only for testing purposes and you are making it less safe in case of inheritance.
#include
class ToBeTested
{
protected:
int Calculate() { return 0; }
};
class UnitTestClass : ToBeTested
{
public:
void RunTest()
{
if (Calculate() == 0)
{
std::cout << "Test passed" << std::endl;;
}
else
{
std::cout << "Test failed" << std::endl;;
}
}
};
int main()
{
UnitTestClass unit_tester;
unit_tester.RunTest();
return 0;
}
Move It to a Separate Class
The solution which is letting your code done in the most object oriented and in the most clean way is to move the function to be tested into a new class. So if you have for example, a complex Calculate
private
function in your class, which should be unit tested, just move it to a new Calculator
class. Or to a class which is a collection of helper functions. And add a private
member of your new class to your old class. In this way, your code will be more testable, reusable and thanks to technologies like dependency injection, you can exchange your algorithm in a more elegant way in the future and you can also easily mock it to unit test the other parts of your class. This is the solution which I prefer, since in this way, you will have smaller classes, with clear, well-defined responsibility.
#include
class UnitTestClass;
class Calculator
{
int GetResult() { return 0; }
};
class ToBeTested
{
private:
Calculator calculator;
};
class UnitTestClass
{
public:
void RunTest()
{
Calculator calculator_under_test;
if (calculator_under_test.GetResult() == 0)
{
std::cout << "Test passed" << std::endl;
}
else
{
std::cout << "Test failed" << std::endl;;
}
}
};
int main()
{
UnitTestClass unit_tester;
unit_tester.RunTest();
return 0;
}
Summary
There are several other hacks which enable you to test private
functions, like playing with preprocessor directives, or use FRIEND_TEST
option of GTEST
, but at the end of the day, these are all just versions of the solutions which I previously mentioned.
The best and most preferred solution is absolutely language independent: make your architecture clean. This is always a good way to create nice software.
History
- 28th October, 2019: Initial version