Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unit testing - setting private members to get desired object state

I'm taking my first steps with unit testing and have a problem with encapsulation. My class has some private member variables that shouldn't be visible to the client, but in order for me to put object in a state I want to test it under, I need to set those private variables.

Say I have a code like that:

Class Foo {

public:
  int action() ;  
private:
  int state ;

} ;

int Foo::action()
{
  if(this->state == 1)
    return 1 ;
  else
    return 0 ;
}

So now I want to test Foo::action(), but I need to be able to set Foo::state to be able to check function under different scenarios. One solution is the evil "define private public" in tests code. But is there something more elegant? I would like to stress that Foo::state is a variable that shouldn't be accessed by client, so I don't want to declare any public setter.

Edit:

I now think that extending the class I want to test in test code and including setters in that derived class would work, providing I changed private variables to protected. But that's a 'one generation only' solution and still feels like a hack rather than a proper approach.

Edit 2:

After reading answers and comments I was given (thanks to Lieven and ap. in particular) I believe the actual class I'm trying to test now (not the simple example I provided) simply does too much and the answer to my problem is moving some of its logic into another class that will be used by the big guy.

like image 310
Puchatek Avatar asked Dec 20 '12 11:12

Puchatek


People also ask

How do you test private methods in unit testing?

To test private methods, you just need to test the public methods that call them. Call your public method and make assertions about the result or the state of the object. If the tests pass, you know your private methods are working correctly.

Should private methods have unit tests?

Unit Tests Should Only Test Public Methods The short answer is that you shouldn't test private methods directly, but only their effects on the public methods that call them. Unit tests are clients of the object under test, much like the other classes in the code that are dependent on the object.

Which method given in the options is used for unit testing?

Unit tests can be performed manually or automated. Those employing a manual method may have an instinctual document made detailing each step in the process; however, automated testing is the more common method to unit tests.


2 Answers

There are only two posibilities (refactoring asside)

  1. Use the public interface to set the state.
  2. The state is redundant if you can't set it to through the public interface.

Option 2 is self explanatory and most likely not applicable to your case so you are left with setting the state through the public interface of your class.

As you have already mentioned, this is possible but it requires a lot of code to get to the right state. That in itself could be an indication that your class is currently doing to much and it's time to refactor parts of your class into smaller, testable classes.

From Do not test private methods

If you find the need to test a private method, then you’re doing something else wrong. There is an “upstream” problem, so to speak. You have arrived at this problem due to some other mistake previous in this process. Try to isolate what that is, exactly, and remove that rather than bending your tests to go down a painful road of brittleness in testing.

and Unit testing private members

I'd recommend not unit testing private methods. Since they're private, they may be changed in any conceivable (or inconceivable?) manner between each release. If a given private method is so critical to the operation of the class that you feel it deserves test cases, then it's probably time to refactor that out into a protected or public method

A common quote on this is

You should never touch your privates

like image 60
Lieven Keersmaekers Avatar answered Oct 06 '22 00:10

Lieven Keersmaekers


If your test class is called MyTestClass, then add MyTestClass as friend in class Foo to be able to access its private member variables.

Class Foo {
public:
    int action();  
private:
    int state;

    friend class MyTestClass;
};
like image 30
xavatage Avatar answered Oct 06 '22 01:10

xavatage