【问题标题】:Accessing protected member functions from test code in C++从 C++ 中的测试代码访问受保护的成员函数
【发布时间】:2010-12-15 23:44:51
【问题描述】:

我一直在绞尽脑汁想从 C++ 中的一些测试代码中访问受保护成员函数的最佳方法,这是我的问题:

//in Foo.h 
Class Foo
{
protected:
    void DoSomething(Data data);
}

//in Blah.h
Class Blah
{
public:
    Foo foo;
    Data data; 
};

//in test code...
Blah blah;
blah.foo.DoSomething(blah.data); // Here's my problem!

目前为止的一些可能的解决方案:

  • 让测试代码类成为 Foo 的朋友,但这会污染 Foo 与测试代码
  • 将 DoSomething 设为公共函数
  • 按照this post 中的建议,我已经研究过为Foo 创建一个测试包装器,但是这不起作用,因为Blah 包含Foo 的实例。

    欢迎所有建议/见解/意见!

    谢谢

  • 【问题讨论】:

    • 为什么要测试受保护的方法?我相信你应该测试类的公共接口,将内部实现留给类本身。
    • @AraK,很抱歉看到您删除了答案。这几乎是正确的,但是您应该使用&test::DoSomething 而不是&Foo::DoSomething - 这是该技巧中的重要内容,因为它使访问检查顺利进行。既然您是第一个回答的,请告诉我您是否想取消删除您的答案,我将在下面删除我的答案!
    • 嗯,你为什么不直接创建一个派生类呢?我在这里看到的每一个答案都破坏了面向对象的整个目的。继承的存在是有原因的!

    标签: c++ testing protected


    【解决方案1】:

    与其将private 定义为public,不如考虑ifdefing 友谊,或者更好地考虑该函数真的是否需要属于该类,也许它会在 cpp 中的命名/未命名命名空间中有一些东西就足够了,然后在测试项目中声明。

    无论如何,请检查this 链接,也许您的测试框架会提供类似的功能。

    编辑:你考虑过从你的真实类继承你的测试类吗?

    【讨论】:

      【解决方案2】:

      有一种标准完全允许的方式。

      //in Foo.h 
      class Foo
      {
      protected:
          void DoSomething(Data data);
      };
      
      //in Blah.h
      class Blah
      {
      public:
          Foo foo;
          Data data; 
      };
      
      //in test code...
      struct FooExposer : Foo {
        using Foo::DoSomething;
      };
      
      Blah blah;
      (blah.foo.*&FooExposer::DoSomething)(blah.data);
      

      阅读Hidden features of C++ 条目以获得解释。


      为方便起见,您可以编写一个宏(括号在那里,以便您可以将此宏也用于具有逗号的类型,例如vector<pair<A, B>>):

      #define ACCESS(A, M, N) struct N : get_a1<void A>::type { using get_a1<void A>::type::M; }
      
      template<typename T> struct get_a1;
      template<typename R, typename A1> struct get_a1<R(A1)> { typedef A1 type; };
      

      事情变成了

      ACCESS((Foo), DoSomething, GetDoSomething);
      Blah blah;
      (blah.foo.*&GetDoSomething::DoSomething)(blah.data);
      

      【讨论】:

      • 确实不错。可惜它不适用于 private 方法。
      • 对于那些想要这样做的人,从上面获取模板,然后添加:#define ACCESS(A, M) struct M##_struct : get_a1::type {使用 get_a1::type::M; #define CALL(B, M) ((B).*&M##_struct::M) 要使用它:在程序顶部执行 ACCESS((Foo), DoSomething) 。然后执行 CALL(foo, DoSomething)(arg1, arg2) 调用受保护的方法。
      • this 在 VS2015 更新 3 中失败:error C2440: '.*': cannot convert from 'A *' to 'B *',仅适用于成员函数,对于成员变量可以正常编译
      • 其实VS2012和VS2013也不能编译,这个VC++编译器不符合C++标准吗?
      • @AndyT 我不知道。我怀疑这是不合格的。但是我写代码太久了,所以我忘记了。
      【解决方案3】:

      如下使用包装器:

      // Foo.h unchanged
      
      // Blah.h unchanged
      
      // test code
      class FooTest : public Foo { friend void test(); }; // make friends
      
      void test()
      {
          Blah blah;
          static_cast<FooTest*>(&blah.foo)->DoSomething(blah.data); // Here's no problem!    
      }
      

      【讨论】:

        【解决方案4】:

        您可以将继承与转发功能一起使用:

        class Foo
        {
        protected:
            void DoSomething(Data data);
        }
        
        class test_Foo : public Foo
        {
        public:
            void testDoSomething(Data data)
            {
                DoSomething(data);
            }
        }
        

        【讨论】:

        • 您可能希望将其与依赖注入结合起来,这样您就可以将 Blah 中的 Foo 替换为 test_Foo 实例。
        【解决方案5】:

        好的,既然你说这只是一个测试代码,我将建议一些非常hacky但可以工作的东西:

        struct tc : protected Foo
        {
            tc(Foo *foo, Data& data)
            {
                ((tc*)foo)->DoSomething(data);
            }
        };
        
        Blah blah;
        tc t(&blah.foo, blah.data);
        

        【讨论】:

        • 感谢所有伟大的建议。我决定暂时采用这个解决方案。
        【解决方案6】:

        一方面,不要那样做。

        另一方面,这是一场赌博:

        #define protected public
        #include "foo.h"
        #undef protected
        

        8-)

        但是说真的,为什么DoSomething() 受到保护?可能是因为从外部代码调用它会破坏某些东西。在这种情况下,您不应该从测试中调用它。

        【讨论】:

        • 如果您绝对必须测试受保护的成员(我同意,您不应该这样做),加好友不是更好的方式。
        • @Dominic:是的,但@sarah 明确不想使用friend。
        • 当你有你想练习的课程的标题和库但没有源时,这个技巧(有时)有效。
        • (公平地说,您不建议这样做,因此此评论并不反对您的回答,而只是反对该技术)。当“foo.h”实际上只是一个只有一个类而没有其他类的文件时,这可能有效,因此您可以完全控制它。但我不会这样做,因为标题间接包含的任何内容也会受到影响。可能是另一段代码。它可能会导致不同的 ABI 接口,从而导致奇怪的效果 - 你永远不知道编译器在内部做了什么 - 也许它通过成员的可访问性或其他东西来排序成员。
        • @RichieHindle - 我将首先重新评估该功能是否需要保护。谢谢你的建议:)
        【解决方案7】:

        我已经完成了

        class Foo
        {
        protected:
            void DoSomething(Data data);
        public:
            #ifdef TEST
            void testDoSomething(Data data);
            #endif
        }
        

        然后用 g++ -D TEST 编译你的单元测试。

        【讨论】:

          【解决方案8】:

          如果是严格的测试代码,你可以这样做......

          #define protected public
          #include "Foo.h"
          
          // test code
          
          #undef protected
          

          【讨论】:

            猜你喜欢
            • 2014-06-07
            • 2016-04-07
            • 1970-01-01
            • 2013-11-30
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2012-11-27
            • 2017-08-01
            相关资源
            最近更新 更多