【问题标题】:Does Mock Objects in C++ Always Requires Virtual Methods or Templates?C++ 中的模拟对象是否总是需要虚拟方法或模板?
【发布时间】:2011-07-22 12:24:32
【问题描述】:

假设我有课

class Inner {
  public:
    void doSomething();
};

class Outer {
  public:
    Outer(Inner *inner);  // Dependency injection.

    void callInner();
};

正确的单元测试表明我应该对Inner 进行测试。然后,我应该对Outer 进行测试,它使用的不是真正的Inner,而是MockInner,这样我就可以对Outer 添加的功能执行单元测试,而不是完整的堆栈Outer /Inner.

为此,Googletest 似乎建议将Inner 变成这样的纯抽象类(接口):

// Introduced merely for the sake of unit-testing.
struct InnerInterface {
  void doSomething() = 0;
};

// Used in production.
class Inner : public InnerInterface {
  public:
    /* override */ void doSomething();
};

// Used in unit-tests.
class MockInner : public InnerInterface {
  public:
    /* override */ void doSomething();
};

class Outer {
  public:
    Outer(Inner *inner);  // Dependency injection.

    void callInner();
};

所以,在生产代码中,我会使用Outer(new Inner);在测试中,Outer(new MockInner)

好的。理论上看起来不错,但是当我开始在整个代码中使用这个想法时,我发现自己为每个该死的类创建了一个纯抽象类。即使您可以忽略由于不必要的虚拟调度而导致的轻微运行时性能下降,这是很多样板类型。

另一种方法是使用以下模板:

class Inner {
  public:
    void doSomething();
};

class MockInner {
  public:
    void doSomething();
};

template<class I>
class Outer {
  public:
    Outer(I *inner);

    void callInner();
};

// In production, use
Outer<Inner> obj;

// In test, use
Outer<MockInner> test_obj;

这避免了样板化和不必要的虚拟调度;但是现在我的整个代码库都在该死的头文件中,这使得隐藏源代码实现变得不可能(更不用说处理令人沮丧的模板编译错误和漫长的构建时间了)。

虚拟和模板这两种方法是进行正确单元测试的唯一方法吗?有没有更好的方法来进行正确的单元测试?

通过适当的单元测试,我的意思是每个单元测试只测试该单元引入的功能,而不测试该单元的依赖项

【问题讨论】:

    标签: c++ unit-testing mocking googletest


    【解决方案1】:

    我认为您必须在实践中模拟出您测试类的每个依赖项。如果创建、使用或感知它很复杂,那么是的。此外,如果它直接依赖于一些不需要的外部资源,例如数据库、网络或文件系统。

    但如果这些都不是问题,IMO 可以直接使用它的一个实例。由于您已经对其进行了单元测试,因此您可以合理地确定它按预期工作并且不会干扰更高级别的单元测试。

    我个人更喜欢工作单元测试和简单、干净、可维护的设计,而不是坚持单元测试纯粹主义者的一些理想设置。

    每个单元测试仅测试该单元引入的功能,但也不测试该单元的依赖项。

    使用功能测试功能是两件截然不同的事情。

    【讨论】:

    • 我个人更喜欢工作单元测试和简单、干净、可维护的设计,而不是坚持单元测试纯粹主义者的一些理想设置。我喜欢这种回应---保持实际。
    【解决方案2】:

    我也认为直接在 Inner 上使用实例是可以的。 我的问题是模拟不属于我的代码的外部对象(通过静态库或 DLL,有时是第 3 方提供)。 我倾向于用相同的类名重写一个模拟 DLL 或库,然后以不同的方式链接进行测试。修改外部依赖的头文件以添加“虚拟”对我来说似乎是不可接受的。 谁有更好的解决方案?

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-09-28
      • 2011-12-19
      • 2012-02-14
      • 2014-10-25
      • 1970-01-01
      • 2016-04-12
      • 2021-06-16
      相关资源
      最近更新 更多