Click here to Skip to main content
15,885,546 members
Articles / DevOps / Unit Testing
Tip/Trick

How to Unit Test a Private Function in C++?

Rate me:
Please Sign up or sign in to vote.
4.38/5 (6 votes)
28 Oct 2019CPOL3 min read 39.4K   3   4
This tip shows how to unit test a private function in C++.

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.

C++
#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:

C++
#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.

C++
#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.

C++
#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

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
Germany Germany
I'm a software developer with around 5 years experience. Actually my main focuses are the following: software architecture, technical project leading, coaching, quality assurance and C++ development.
Next to that I'm blogging under: http://howtosurviveasaprogrammer.blogspot.com

Comments and Discussions

 
SuggestionMore generally... Pin
Greg Utas1-Nov-19 8:06
professionalGreg Utas1-Nov-19 8:06 
SuggestionForward declaration Pin
Ruslan Minyukov29-Oct-19 11:06
Ruslan Minyukov29-Oct-19 11:06 
Suggestion"public" is missed Pin
Ruslan Minyukov29-Oct-19 10:58
Ruslan Minyukov29-Oct-19 10:58 
GeneralRe: "public" is missed Pin
Marcell Lipp29-Oct-19 11:02
Marcell Lipp29-Oct-19 11:02 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.