简体   繁体   English

单元测试访问私有变量

[英]Unit test accessing private variables

I have a unit test class Tester ; 我有一个单元测试类Tester ; I want it to access private fields of a Working class. 我希望它访问Working类的私有字段。

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 使m_variable public - 丑陋
  • make method test_getVariable() - overcomplicated make method test_getVariable() - 过于复杂
  • add friend class Tester to Working - then Working "knows" about the Tester explicitly, which is not good friend class Tester添加到工作中 - 然后明确地“明白”关于测试程序,这是不好的

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. Working知道TestBase而不是每个测试...但它不起作用。 Apparently friendship does not work with inheritance. 显然友谊不适用于继承。

What would be the most elegant solution here? 这里最优雅的解决方案是什么?

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. 如果您确实需要检查私有变量是否具有特定特性,请考虑使用assert()而不是尝试为其编写单元测试。

A longer answer (written for C# rather than C++, but the same principles apply) is at https://stackoverflow.com/a/1093481/436641 . 更长的答案(为C#而不是C ++编写,但适用相同的原则)在https://stackoverflow.com/a/1093481/436641

I agree with Trott's answer , but sometimes you're adding unit tests to legacy code that wasn't designed for it. 我同意Trott的回答 ,但有时您会将单元测试添加到非为其设计的遗留代码中。 In those cases, I reach for #define private public . 在那些情况下,我到达#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. 这是丑陋的,技术上非法,非常有效。

-fno-access-control

If you're only using GCC, you can use the compiler option -fno-access-control while compiling your unit tests. 如果您只使用GCC,则可以在编译单元测试时使用编译器选项-fno-access-control This will cause GCC to skip all access checks, but still keep the class layout the same. 这将导致GCC跳过所有访问检查,但仍保持类布局相同。 I don't know if there is a similar option for other compilers, so this isn't a general solution. 我不知道其他编译器是否有类似的选项,所以这不是一般解决方案。

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 需要测试的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 is only available to unit test code. 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 )
      );
}

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

If you absolutely must do this, you could conditionally compile your code so that TestBase is a friend only when unit testing: 如果你绝对必须这样做,你可以有条件地编译你的代码,这样TestBase只有在单元测试时才是朋友:

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

#ifdef UNIT_TESTING
    friend class TestBase;
#endif
};

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: 该副本由测试目录中的makefile生成,以便在原始更改时重新生成副本:

 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. 'test'make目标取决于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. 这允许访问测试中的所有私有成员变量,即使真实库是使用这些成员变量私有编译的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM