假/模拟非虚拟C++方法

zah*_*pov 21 c++ testing mocking

众所周知,在C++中,模拟/伪造非虚拟方法进行测试很难.例如,googlemock的cookbook有两个建议 - 两者都意味着以某种方式修改原始源代码(模板化和重写为界面).

对于C++代码来说,这似乎是一个非常糟糕的问题.如果你不能修改需要伪造/嘲笑的原始代码,怎么办才能做得最好?复制整个代码/类(用它整个基类层次结构?)

sdg*_*sdg 11

我们有时使用的一种方法是将原始.cpp文件拆分为至少两部分.

然后测试设备可以提供自己的实现; 有效地使用链接器为我们做脏工作.

这在某些圈子中被称为" Link Seam ".

  • +1 - 我们也这么做了。一份 foobar.cpp 包含实际实现,一份 foobar_ut.cpp 包含用于单元测试的模拟存根。 (2认同)

Pet*_*hev 8

我从sdg的回答中跟踪了Link Seam链接.在那里我读到了不同类型的接缝,但我对预处理接缝印象最深刻.这让我想到了进一步利用预处理器.事实证明,可以模拟任何外部依赖,而无需实际更改调用代码.

为此,您必须使用替换依赖项定义编译调用源文件.这是一个如何做到的例子.

dependency.h

#ifndef DEPENDENCY_H
#define DEPENDENCY_H

class Dependency
{
public:
    //...
    int foo();
    //...
};

#endif // DEPENDENCY_H
Run Code Online (Sandbox Code Playgroud)

caller.cpp

#include "dependency.h"

int bar(Dependency& dependency)
{
    return dependency.foo() * 2;
}
Run Code Online (Sandbox Code Playgroud)

TEST.CPP

#include <assert.h>

// block original definition
#define DEPENDENCY_H

// substitute definition
class Dependency
{
public:
    int foo() { return 21; }
};

// include code under test
#include "caller.cpp"

// the test
void test_bar()
{
    Dependency mockDependency;

    int r = bar(mockDependency);

    assert(r == 42);
}
Run Code Online (Sandbox Code Playgroud)

请注意,mock不需要实现完整Dependency,只需要最小(由caller.cpp使用),因此测试可以编译和执行.这样,您可以在不更改生产代码的情况下模拟非虚拟,静态,全局函数或几乎任何依赖项.我喜欢这种方法的另一个原因是与测试相关的所有内容都在一个地方.您不必在此处调整编译器和链接器配置.

我已经成功地将这项技术应用于具有大量依赖性的真实世界项目中.我在Include mock中有更详细的描述.

  • 您可以在另一个编译单元中对其进行测试,例如在test2.cpp中 (2认同)

Ste*_*sop 7

必须使用您使用的任何测试技术编写代码以使其可测试.如果你想使用模拟测试,这意味着某种形式的依赖注入.

与模板参数不依赖非虚拟呼叫造成同样的问题,finalstatic在Java方法[*] -被测代码已经明确表示,"我想打电话给这个代码,代码不是一些未知位这是依赖于一些关于论证的方式".作为测试人员,您希望它从正常调用的内容中调用不同的代码.如果您无法更改测试中的代码,那么您(测试人员)将失去该参数.您还可以询问如何在不更改测试代码的情况下引入10行函数的第4行的测试版本.

如果要模拟的类与被测试的类位于不同的TU中,则可以编写与原始类相同的模拟,并将其链接起来.无论你是否能以正常方式使用模拟框架生成模拟,我都不太确定.

如果你愿意,我认为这是一个"非常糟糕的C++问题",它可以编写难以测试的代码.它与许多其他语言共享这个"问题"......

[*]我的Java知识非常低.在Java中可能有一些巧妙的方法来模拟这样的方法,这些方法不适用于C++.如果是这样,请忽略它们以便看到类比;-)


Dan*_*röm -2

我曾经为我需要模拟的部分创建一个界面。然后,我简单地创建了一个从该接口派生的存根类,并将该实例传递给我的测试类。是的,这是一项艰苦的工作,但我发现在某些情况下这是值得的。

哦,我所说的接口是指struct具有纯虚方法的接口。没有其他的!

  • 丹尼尔,抱歉,但它没有回答问题 - 请再读一遍,它说“如果你不能修改原始代码” (2认同)