【问题标题】:Override non-virtual function for testing purpose覆盖非虚拟功能以进行测试
【发布时间】:2014-05-30 14:31:13
【问题描述】:

我有一个需要模拟的类,但该类有需要模拟的非虚拟方法。我的问题的简单版本如下所示:

#include <iostream>
#include <string>

using namespace std;

class Base{
   public:
    void getNext(){
        cout<<"Base Func"<<endl;
    }
};

class Derived: public Base{
   public:
    void getNext(){
        cout<<"Derived func"<<endl;
    }
};

int main(){
   Base *ptr = new Derived();
   ptr->getNext();
}

上面的代码将调用基类的“getNext”函数,但我希望它以某种方式调用派生类的。真正的问题是我创建了一个新类,其唯一的公共方法执行一些逻辑并且还需要来自数据库的序列。从数据库中获取序列的类(称为 DlSequence)是我们应用程序(您可以调用的核心或产品)的基本代码,我无法更改它。现在我需要使用 cppunit 测试我的课程。所以,我必须模拟或伪造 DlSequence 类并覆盖 getNext 函数,但这个函数不是虚拟的。可以模拟它吗?或者我可以用任何解决方法绕过这个问题。 我没有使用任何模拟框架,但我的盒子里安装了 mockpp,我无法安装任何其他模拟框架。提前致谢。

【问题讨论】:

  • 注意:这里不需要new,多态不需要动态分配。
  • 为什么不对新的 Derived 实例使用 Derived* 而不是 Base*,否则我错过了什么?
  • @Merlin069:可能是因为要测试的东西需要Base*Base&amp;,因为Derived 仅用于模拟目的。
  • 你能让你的课程template 在实际代码中使用Base,在你的测试中使用MockedBase 吗?
  • @MatthieuM.,嗯,有道理。谢谢;O)

标签: c++ tdd overriding virtual cppunit


【解决方案1】:

简短的回答:

如果类的作者没有提供自定义行为的方法,那么这是不可能的。

如果您自己无法更改,请向他们提出您需要挂钩的问题。


长答案:

就 C++ 而言,可能有一些方法可以尝试破解它,但它既不安全又脆弱。在我的脑海中,我可以想到基于预处理器hackery 在基类中注入virtual 关键字,甚至使用 LD_PRELOAD 来覆盖函数符号......但老实说,两者都没有吸引力。

注意:如果我们正在讨论测试您的代码,并且您可以更改您的代码,那么您当然可以围绕这个 Base 事物创建一个包装器并使其成为 virtual/template/whatever ;我想在这里你不能。

然而,就 SQL 而言,您可能有机会,例如将 API 调用甚至网络调用重定向到您控制的模拟或数据库实例。不知道您的系统的具体情况,但我无法提供任何进一步的建议。

【讨论】:

  • 另一个不吸引人的想法可能会或可能不会取决于一堆因素是在测试项目中链接到 Base::getNext() 的不同实现。
  • @dlf:这实际上是 LD_PRELOAD 的意义所在(这是 Unix 特有的),您首先加载带有几个“hack”符号(例如 Base::getNext())的库,然后当您下一步加载真正的库,它不会替换现有符号,因此您成功插入了您的(但只有少数)。
  • @MatthieuM.:谢谢,我理解简短和详细的答案。我可以在我的代码中在 Base 上创建一个包装器并将其设为虚拟,我避免使用它以了解问题是否也可以通过其他方式解决。
【解决方案2】:

当您测试需要与外部资源(例如,第三方库、I/O 等)交互的代码时,您通常可以在 introduce a seam 处模拟依赖项。

这也适用于您的情况。如果您无法修改协作者的代码,那么它与 3rd 方库(在这个意义上)没有太大区别。与其尝试直接模拟协作者,不如找出您的代码和有问题的代码之间的接缝。

例如:

    #include <iostream>
    #include <string>

    using namespace std;

    class Base{
    public:
        void getNext(){
            cout << "Base Func" << endl;
        }
    };

    class Seam{
    public:
        virtual ~Seam() { }

        virtual void getNext() = 0;
    };

    class MockCollaborator : public Seam{
    public:
        void getNext(){
            cout << "Derived func" << endl;
        }
    };

    class RealCollaborator : public Seam{
    public:
        void getNext(){
            Base base;
            base.getNext();
        }
    };

    int main(){
        Seam* ptr = new MockCollaborator();
        ptr->getNext();
    }

【讨论】:

    【解决方案3】:

    如果你可以制作你的类模板,你可以使用类似下面的东西:

    class Base {
    public:
        void getNext() { cout << "Base Func" << endl; }
    };
    
    // Has the same interface of Base
    class MockedBase {
    public:
        void getNext() { cout << "Derived func" << endl; }
    };
    

    在你的课堂上,做这样的事情:

    template <typename T> class myClass
    {
    public:
        void callGetNext() { base.getNext(); }
    private:
        T base;
    };
    

    所以在实际代码中使用myClass&lt;Base&gt;,在单元测试中使用myClass&lt;MockedBase&gt;

    【讨论】:

    • 谢谢,模板是个好主意。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-19
    • 1970-01-01
    • 2014-05-12
    • 2015-10-30
    • 2020-04-01
    • 1970-01-01
    相关资源
    最近更新 更多