8

I have a unit test class Tester; I want it to access private fields of a Working class.

class Working {
    // ...
    private:
    int m_variable;
};

class Tester {
    void testVariable() {
        Working w;
        test( w.m_variable );
    }
}

I have the following options:

  • make m_variable public - ugly
  • make method test_getVariable() - overcomplicated
  • add friend class Tester to Working - then Working "knows" about the Tester explicitly, which is not good

My ideal would be

class Working {
    // ...
    private:
    int m_variable;

    friend class TestBase;
};

class TestBase {};

class Tester : public TestBase {
    void testVariable() {
        Working w;
        test( w.m_variable );
    }
}

where Working knows about TestBase but not each test... but it does not work. Apparently friendship does not work with inheritance.

What would be the most elegant solution here?

Jakub M.
  • 32,471
  • 48
  • 110
  • 179
  • 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:37

6 Answers6

15

I agree with Trott's answer, but sometimes you're adding unit tests to legacy code that wasn't designed for it. In those cases, I reach for #define private public. It's just in unit tests, and it's just for when refactoring is too expensive to bother. It's ugly, technically illegal, and very effective.

Community
  • 1
  • 1
Michael Kristofik
  • 34,290
  • 15
  • 75
  • 125
  • 2
    Even then, I would go with `-D private=public` or some similar compiler statement. – Etienne de Martel Dec 23 '11 at 17:14
  • 4
    Really? That moves the magic that allows the test to compile away from the code that needs it. I wouldn't want to bury that sort of thing in the build system somewhere. I like my ugly hacks front and center where everyone can see them. – Michael Kristofik Dec 23 '11 at 17:18
  • 3
    And, if you `#undef private` right after the `include that needs it, you can limit the damage to one place. – Michael Kristofik Dec 23 '11 at 17:19
  • 2
    Haha, [if John Carmack can do it, so can you!](https://github.com/TTimo/doom3.gpl/blob/master/neo/game/Game_local.h) :D – Kos Dec 23 '11 at 17:51
  • 2
    @Kristo has it right. The initial tests on **Legacy Code** is not a true Unit Test. It is a test so that as you change dependencies and such, you can feel confident that you are not breaking code. – Gutzofter Dec 23 '11 at 18:01
14

Generally, your unit tests should not evaluate private variables. Write your tests to the interface, not the implementation.

If you really need to check that a private variable has a particular characteristic, consider using assert() rather than trying to write a unit test for it.

A longer answer (written for C# rather than C++, but the same principles apply) is at https://stackoverflow.com/a/1093481/436641.

Community
  • 1
  • 1
Trott
  • 66,479
  • 23
  • 173
  • 212
  • 4
    Statements like this (one shouldn't ever be testing private interfaces) puzzle me. Let me probe a bit: Take, for example, a factory which should return lent resources to the free pool when all clients have released them. Since there's no need to expose pool state to clients, it is private. How should one verify correct behavior in situations like this? (As per any case where behavior changes, I would expect these tests to need refactoring if someone later came along and implemented a different scheme. In fact, they'd light up red and tell the implementer, which would be by design.) – U007D Oct 18 '16 at 16:47
  • FWIW, the answer now says "Generally" to make it clear that there are exceptions. – Trott Sep 02 '21 at 23:35
5

-fno-access-control

If you're only using GCC, you can use the compiler option -fno-access-control while compiling your unit tests. This will cause GCC to skip all access checks, but still keep the class layout the same. I don't know if there is a similar option for other compilers, so this isn't a general solution.

Community
  • 1
  • 1
deft_code
  • 57,255
  • 29
  • 141
  • 224
3

Try very hard to test all your private code using your public interface. Not only is it less work initially, but when you change the implementation there is much higher chance that the unit tests will still work.

That said, sometime you just need to poke at the innards to get good test coverage. In that case I use an idiom I call expose. There is a joke in there if you think about it.

Foo class that needs to be tested

class Foo
{
public:
   // everyone is on their honor to only use Test for unit testing.
   // Technically someone could use this for other purposes, but if you have
   // coders purposely doing bad thing you have bigger problems.
   class Test;

   void baz( void );

private:
   int m_int;
   void bar( void );
};

foo_exposed.h is only available to unit test code.

class Foo::Test : public Foo
{
public:
   // NOTE baz isn't listed

   // also note that I don't need to duplicate the
   // types / signatures of the private data.  I just
   // need to use the name which is fairly minimal.

   // When i do this I don't list every private variable here.
   // I only add them as I need them in an actual unit test, YAGNI.

   using Foo::m_int;
   using Foo::bar;
};


// yes I'm purposely const smashing here.
// The truth is sometimes you need to get around const
// just like you need to get around private

inline Foo::Test& expose( const Foo& foo )
{
   return * reinterpret_cast<Foo::Test*>(
         &const_cast<Foo::Test&>( foo )
      );
}

How it would be used in unit test code

#include "foo_exposed.hpp"

void test_case()
{
   const Foo foo;

   // dangerous as hell, but this is a unit test, we do crazy things
   expose(foo).m_int = 20;
   expose(foo).baz();
}
deft_code
  • 57,255
  • 29
  • 141
  • 224
  • "when you change the implementation there is much higher chance that the unit tests will still work." - Can you explain to me why you want this, as all I'm thinking is if you make a change that is going to break something, you want unit tests to tell you exactly how this is happening. also, the highest chance of all your unit tests working is just writing one test saying assert.istrue(true), so I don't think that maximizing unit test work chances is necessarily good. – puser Nov 27 '13 at 18:21
  • Say you wrote a scheduler that was implemented with a list. Later you find performance issues and switch to an array. All of the same features will need to be tested. But if the original test relied on the list from the implementation details, they'll no longer compile. You'll have to rewrite them or scrap them altogether. However, if you wrote unit tests that only touched the public interface, those same unit tests should just work, and if you're lucky they'll still provide sufficient coverage. – deft_code Nov 30 '13 at 17:57
  • hmm. any tests of private functions that would fail because of the change from list to array should still cause failures in the public functions tests wouldn't it? The only advantage I'm seeing is you can do less tests, which could mean you are being less thorough. Also, IMO seeing a bunch of failed tests is a good thing as you now know more detail into what problems you may have caused. – puser Dec 02 '13 at 09:41
2

If you absolutely must do this, you could conditionally compile your code so that TestBase is a friend only when unit testing:

class Working {
    // ...
    private:
    int m_variable;

#ifdef UNIT_TESTING
    friend class TestBase;
#endif
};
Dave Rager
  • 8,002
  • 3
  • 33
  • 52
0

I did this by using a copy of my class header file in my test that is missing the "private" access specifier. The copy is generate by the makefile in the test directory so that the copy is regenerated if the original changes:

 perl -ne 'print unless m/private:/;' < ../include/class_header.h > mock_class_header.h

and the 'test' make target depends on mock_class_header.h.

This grants access to all the private member variables in the test, even though the real library was compiled with these member variables being private.

Unacoder
  • 71
  • 3