单元测试访问私有变量

时间:2011-12-23 17:03:35

标签: c++ unit-testing

我有一个单元测试班Tester;我希望它访问Working类的私有字段。

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

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

我有以下选择:

  • 制作m_variable public - 丑陋
  • make method test_getVariable() - 过度复杂的
  • friend class Tester添加到工作中 - 然后明确地工作“知道”测试人员,这不是很好

我的理想是

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

    friend class TestBase;
};

class TestBase {};

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

在哪里工作了解TestBase而不是每个测试...但它不起作用。显然友谊不适用于继承。

这里最优雅的解决方案是什么?

6 个答案:

答案 0 :(得分:16)

通常,您的单元测试不应评估私有变量。将测试写入接口,而不是实现。

如果您确实需要检查私有变量是否具有特定特征,请考虑使用assert()而不是尝试为其编写单元测试。

更长的答案(为C#而不是C ++编写,但适用相同的原则)在https://stackoverflow.com/a/1093481/436641

答案 1 :(得分:14)

我同意Trott's answer,但有时您会将单元测试添加到非为其设计的遗留代码中。在这些情况下,我会到达#define private public。它只是在单元测试中,而且只是因为重构过于昂贵而无法理解。它很丑陋,技术上非法,非常有效。

答案 2 :(得分:4)

-fno-access-control

如果您只使用GCC,则可以在编译单元测试时使用编译器选项-fno-access-control。这将导致GCC跳过所有访问检查,但仍保持类布局相同。我不知道其他编译器是否有类似的选项,所以这不是一般的解决方案。

答案 3 :(得分:3)

尽量使用公共界面测试所有私有代码。最初它不仅工作量减少,而且更改实现时,单元测试的可能性仍然高得多。

那就是说,有时候你只需要戳内脏来获得良好的测试覆盖率。在这种情况下,我使用一个成语,我称之为公开。如果你考虑一下就会有一个笑话。

需要测试的Foo类

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仅适用于单元测试代码。

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

如何在单元测试代码中使用

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

答案 4 :(得分:2)

如果您绝对必须这样做,您可以有条件地编译您的代码,以便TestBase仅在单元测试时才是朋友:

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

#ifdef UNIT_TESTING
    friend class TestBase;
#endif
};

答案 5 :(得分:0)

我是通过在我的测试中使用我的类头文件的副本来完成此操作,该文件缺少“私有”访问说明符。该副本由测试目录中的makefile生成,以便在原始更改时重新生成副本:

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

和'test'make目标取决于mock_class_header.h。

这允许访问测试中的所有私有成员变量,即使真正的库是使用这些成员变量私有而编译的。