9

Let's say that I have this class in C++:

class ExampleClass{
private:
    int example_var;
public:
    void exampleMethod(){
         example_var = other_value; // other value will be always different
    }
}

How can I unit test exampleMethod()? I would like to do something like this:

void testExampleMethod(){
    ExampleClass obj;
    int before_call_value = obj.example_var;
    obj.exampleMethod();
    int after_call_value = obj.example_var;
    ASSERT_NOT_EQUALS(before_call_value, after_call_value);
}

But example_var is private.

So, what is the right way to do this unit test? How can I test if a private example_var has changed?

Maximetinu
  • 149
  • 3
  • 12
  • 1
    Cant you Implement `get` function for `example_var` ? – kocica Aug 15 '17 at 17:31
  • 2
    Is that really subjective for a unit test? Shouldn't you rather test the behavior of the public interface? I'm not sure that whitebox testing is worth the effort. – user0042 Aug 15 '17 at 17:32
  • By testing observable behavior that results from changing that variable, `friend`ship with the unit-tester, or add a getter in order of my preference. – user4581301 Aug 15 '17 at 17:32
  • 3
    You usually don't. You just unit test public methods. In some way or another they'll use that variable and make it obvious when something's wrong. – Post Self Aug 15 '17 at 17:32
  • if it is hard to test then you have to change it so it can be tested. Whats that `other_value` anyhow? Where does it come from, where does it go? If it cannot be observed from outside then maybe it is an implementation detail that you dont want to test in the first place – 463035818_is_not_an_ai Aug 15 '17 at 17:33
  • Possible duplicate of [How do I test a class that has private methods, fields or inner classes?](https://stackoverflow.com/questions/34571/how-do-i-test-a-class-that-has-private-methods-fields-or-inner-classes) – Raedwald Dec 14 '17 at 13:31

5 Answers5

10

Short answer: Dont do it.

Your test should test against the public interface only. Let me try to explain with some code:

class Adder {
    int a,b;
public:
    Adder() : a(0),b(0) {}
    void set(int x,int y) { a=x;b=y; }
    int get()             { return a+b; }
};

and a test (assume for a moment we had access to a and b):

void testAdder(){
    Adder add;
    int a = 1;
    int b = 2;
    add.set(a,b);
    ASSERT_EQUALS(add.a,a);
    ASSERT_EQUALS(add.b,b);
    ASSERT_EQUALS(add.get(),a+b);
}

Suppose you already distributed the code and someone is using it. He would like to continue using it but complains about too much memory consumption. It is straightforward to fix this issue while keeping the same public interface:

class Adder {
    int c;
public:
    Adder() : c(0) {}
    void set(int x,int y) { c = x+y; }
    int get()             { return c; }
};

That was easy, but the test will fail :(

Conclusion: Testing private implementation details defeats the purpose of testing, because each time you modify the code it is likely that you also have to "fix" the test.

463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185
4

It is bad approach to test private variable/methods. But if you need there are a lot of options:

  1. You can make Your test class as friend of ExampleClass

  2. You can grab information using moc object

arturx64
  • 943
  • 4
  • 12
2

If you want to access example_val, there are one of two things you can do. The first is by making testExampleMethod() a friend method, as follows:

class ExampleClass{
private:
    int example_var;
public:
    void exampleMethod(){
         example_var = other_value; // other value will be always different
    }
    friend void testExampleMethod(); //Now you can use the function as is.
}

On the other hand, you could just add a getter to your ExampleClass to access the variable, such as the following:

class ExampleClass{
private:
    int example_var;
public:
    void exampleMethod(){
         example_var = other_value; // other value will be always different
    }
    inline void getExampleVar() const { return example_var; }
}

And then change testExampleMethod() to:

void testExampleMethod(){
    ExampleClass obj;
    int before_call_value = obj.getExampleVar();
    obj.exampleMethod();
    int after_call_value = obj.getExampleVar();
    ASSERT_NOT_EQUALS(before_call_value, after_call_value);
}

I would honestly use the second method, since accessing a class's private variables is generally not recommended.

Arnav Borborah
  • 11,357
  • 8
  • 43
  • 88
2

You just simply implement get function for that private variable you want to get.

class ExampleClass{
private:
    int example_var;
public:
    void exampleMethod(){
         example_var = other_value; // other value will be always different
    }

    int GetExampleVar(){
         return example_var;
    }
}

And call it like

void testExampleMethod(){
    ExampleClass obj;
    int before_call_value = obj.GetExampleVar();
    obj.exampleMethod();
    int after_call_value = obj.GetExampleVar();
    ASSERT_NOT_EQUALS(before_call_value, after_call_value);
}

Or make testExampleMethod friend function (friend function can access private variables of friend class even if its not its method).

class ExampleClass{
private:
    int example_var;
public:
    void exampleMethod(){
         example_var = other_value; // other value will be always different
    }

    friend void testExampleMethod();
}

In my opinion first example would be more suitable, but if you cannot modify ExampleClass, you can turn off access control for gcc -- -fno-access-control.

kocica
  • 6,412
  • 2
  • 14
  • 35
  • 2
    Adding a getter changes the public API: It exposes state that previously was hidden and, which may have been hidden for a good reason. – Solomon Slow Aug 15 '17 at 17:53
  • Sure its just an one opputurnity. If he has to keep state hidden, he will choose another. – kocica Aug 15 '17 at 17:58
2

A few options I can think of:

1) Make the test code a friend of the class. That way it can access the private members.

2) Add a getter to the class that's under a #ifdef Testing directive that only gets defined when building the test version (or put public: under that macro and a private: in the #else branch).

3) #define private public when building the test (no, not really).

4) Use gcc's -fno-access-control flag when building the version to test, so that everything is public (if you are using gcc that is).

5) Just give up testing externally from the class and instead add relevant static_asserts/asserts to the class itself to test invariants.

6) Don't. Just stick to testing the public interface.

Hope that helps :-)

Jesper Juhl
  • 30,449
  • 3
  • 47
  • 70