Jakub M.
Jakub M.

Reputation: 33827

Unit test accessing private variables

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:

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?

Upvotes: 9

Views: 9071

Answers (6)

Trott
Trott

Reputation: 70075

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.

Upvotes: 14

Unacoder
Unacoder

Reputation: 71

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.

Upvotes: 0

deft_code
deft_code

Reputation: 59269

-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.

Upvotes: 5

deft_code
deft_code

Reputation: 59269

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();
}

Upvotes: 3

Dave Rager
Dave Rager

Reputation: 8150

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
};

Upvotes: 2

Michael Kristofik
Michael Kristofik

Reputation: 35188

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.

Upvotes: 15

Related Questions