【问题标题】:Mock implementations in C++C++ 中的模拟实现
【发布时间】:2011-01-02 00:47:41
【问题描述】:

我需要一个类的模拟实现 - 用于测试目的 - 我想知道我应该如何最好地做到这一点。我可以想到两种一般的方法:

  1. 创建一个包含类的所有公共函数作为纯虚函数的接口,然后通过派生它创建一个模拟类。
  2. 将所有函数(嗯,至少所有要模拟的函数)标记为虚拟

我习惯于在 Java 中使用第一种方式,这也很常见(可能是因为它们具有专用的接口类型)。但我几乎从未在 C++ 中看到过如此繁重的接口设计,因此我想知道。

第二种方法可能会起作用,但我不禁认为它有点丑陋。有人这样做吗?

如果我遵循第一种方式,我需要一些命名帮助。我有一个音频系统,负责加载声音文件和播放加载的曲目。我为此使用 OpenAL,因此我将接口称为“Audio”,将实现称为“OpenALAudio”。但是,这意味着所有特定于 OpenAL 的代码都必须进入该类,这感觉有点限制。另一种方法是保留类的名称“Audio”并为接口找到不同的名称,例如“音频接口”或“IAudio”。你会建议哪一个,为什么?

【问题讨论】:

    标签: c++ testing tdd mocking


    【解决方案1】:

    就像我不会在 Java 中手动编写模拟对象一样,我也不会在 C++ 中手动编写它们。模拟对象不仅是存根的类,而且是执行自动检查的测试工具,例如确保调用某些方法或按顺序调用它们等。我会看看 C++ 的各种模拟对象框架. googlemock 看起来很有趣,但还有其他的。

    关于如何从实现中抽象出控制音频资源的概念,我绝对赞成使用具有通用名称(例如Audio)的 C++“接口”(纯虚拟基类)和以什么命名的实现类让它变得特别(例如OpenALAudio)。我建议你不要在你的类名中嵌入“interface”或“I”这个词。多年来,将类型或编程概念嵌入名称中已不再流行(例如,当您将“接口”提升为成熟的“类”时,可能会强制进行广泛的重命名)。

    开发接口是一个面向对象的概念,因此适用于 C++。一些专门针对 C++ 的最重要的设计书籍都是关于接口编程的(在 C++ 术语中,这意味着使用纯虚拟基类进行编程)。例如,Design PatternsLarge Scale C++ Software Design

    【讨论】:

    • 谢谢,很好的回答,你提到的书当然是很好的榜样:)
    • “大规模 C++ 软件设计”有点过时了。
    【解决方案2】:

    “但我几乎从未在 C++ 中看到过这样的接口密集型设计”,为了您的信息,我建议您简要了解一下 microsoft COM 的操作方式。这种基于 C++ 的技术都是关于重接口设计的。

    界面设计是一种很好的编程方式。如果你习惯了,就这样继续下去。

    如果您发现自己受限于名称,那么使用命名空间是一个好习惯。 而对于接口名称,通常称它们为 ISomething,所以就叫它 IAudio。

    【讨论】:

      【解决方案3】:

      我想说这在很大程度上取决于特定情况和模拟所需的复杂性。我假设理想情况下您会使用原始版本,但由于复杂的依赖关系而希望避免这种情况。然后,在您继续开发测试代码时,可能值得复制粘贴标题并将所有内容注释掉并根据需要重新启用模拟功能。

      我必须承认我没有这方面的实际经验,但在我看来,您对原始代码的干扰越少越好。

      【讨论】:

        【解决方案4】:

        您是否考虑过使用模拟框架,例如 Hippo Mocks

        From the wiki:

        class Foo {
        private:
            IBar *bar;
        public:
            Foo(IBar *bar);
            int a(); //calls IBar::c
        };
        
        class IBar {
        public:
            virtual ~IBar() {}
            virtual void b() = 0;
            virtual int c(std::string) = 0;
        };
        
        void TestAFunctionInFoo() {
            MockRepository mocks;
            IBar *barMock = mocks.InterfaceMock<IBar>();
            Foo *newFoo = new Foo(barMock);
            mocks.ExpectCall(barMock, IBar::c).With("hello").Return(42);
            newFoo->a();
            delete newFoo;
        }
        

        【讨论】:

        • 无论我是手工创建模拟还是使用框架,我的问题仍然相关。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-11-19
        • 1970-01-01
        • 2021-11-09
        • 1970-01-01
        相关资源
        最近更新 更多