Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/129.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
假/假非虚拟C++;方法 < >众所周知,在C++中,模拟和伪造非虚拟方法是很难的。例如,有两个建议-都意味着以某种方式修改原始源代码(模板化和重写为接口) 这似乎是C++代码的一个非常糟糕的问题。如果您不能修改需要伪造/模拟的原始代码,如何才能做到最好?复制整个代码/类(使用整个基类层次结构)?_C++_Testing_Mocking - Fatal编程技术网

假/假非虚拟C++;方法 < >众所周知,在C++中,模拟和伪造非虚拟方法是很难的。例如,有两个建议-都意味着以某种方式修改原始源代码(模板化和重写为接口) 这似乎是C++代码的一个非常糟糕的问题。如果您不能修改需要伪造/模拟的原始代码,如何才能做到最好?复制整个代码/类(使用整个基类层次结构)?

假/假非虚拟C++;方法 < >众所周知,在C++中,模拟和伪造非虚拟方法是很难的。例如,有两个建议-都意味着以某种方式修改原始源代码(模板化和重写为接口) 这似乎是C++代码的一个非常糟糕的问题。如果您不能修改需要伪造/模拟的原始代码,如何才能做到最好?复制整个代码/类(使用整个基类层次结构)?,c++,testing,mocking,C++,Testing,Mocking,我曾为需要模拟的部分创建接口。然后我简单地创建了一个从这个接口派生的存根类,并将这个实例传递给我的测试类。是的,这是一项艰巨的工作,但在某些情况下,我觉得这是值得的 哦,接口我指的是一个struct,只有纯虚拟方法。没有别的了 我们有时使用的一种方法是将原始.cpp文件拆分为至少两部分 然后,测试设备可以提供自己的实现;有效地使用链接器为我们做肮脏的工作 在某些圆中,这被称为“”。这比你想象的要容易。只需将构造的对象传递给正在测试的类的构造函数。在类中存储对该对象的引用。这样就很容易使用模拟类

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


哦,接口我指的是一个
struct
,只有纯虚拟方法。没有别的了

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

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


在某些圆中,这被称为“”。

这比你想象的要容易。只需将构造的对象传递给正在测试的类的构造函数。在类中存储对该对象的引用。这样就很容易使用模拟类

编辑:

传递给构造函数的对象需要一个接口,而该类只存储对接口的引用



在测试中,您可以轻松地通过模拟对象。

代码必须编写为可测试的,无论您使用何种测试技术。如果您想使用mock进行测试,这意味着某种形式的依赖注入

与Java[*]中的
final
static
方法一样,不依赖模板参数的非虚拟调用也会带来同样的问题-测试中的代码明确表示,“我想调用此代码,而不是以某种方式依赖于参数的未知代码位”。您,测试人员,希望它调用与通常调用不同的测试代码。如果您不能更改测试中的代码,那么您(测试人员)将丢失该参数。您还可以询问如何在不更改测试代码的情况下引入10行函数第4行的测试版本

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

如果你愿意,我想这是一个“对于C++来说非常糟糕的问题”,因为它可能会编写难以测试的代码。它与许多其他语言都有这个“问题”

[*]我的Java知识非常低功耗。在java中可能有一些巧妙的方法来嘲笑这种方法,而C++却不适用。如果是这样,请忽略它们以查看类比;-)

您非常明确地说“如果您不能修改原始代码”,您在问题中提到的技术(以及当前所有其他“答案”)就是这样做的

在不更改源代码的情况下,您仍然可以(对于常见的操作系统/工具)预加载一个对象,该对象定义了您希望拦截的函数的自己版本。他们甚至可以在事后调用原始函数。我在(我的)问题中提供了一个这样做的例子。

我跟随了来自的链接。在那里,我读到了不同类型的接缝,但对接缝的预处理给我留下了最深刻的印象。这使我考虑进一步开发预处理器。事实证明,在不实际更改调用代码的情况下,可以模拟任何外部依赖关系

为此,必须使用替代依赖项定义编译调用源文件。 下面是一个如何做到这一点的示例

依赖性

#ifndef DEPENDENCY_H
#define DEPENDENCY_H

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

#endif // DEPENDENCY_H
caller.cpp

#include "dependency.h"

int bar(Dependency& dependency)
{
    return dependency.foo() * 2;
}
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);
}

#include.

@zaharpopov您可以使用Typemock IsolatorPP创建非虚拟类和方法的模拟,而无需更改代码(或遗留代码)。 例如,如果您有一个名为
MyClass
的非虚拟类:

class MyClass
{
 public:
   int GetResult() { return -1; }
}
您可以使用typemock对其进行模拟,如下所示:

MyClass* fakeMyClass = FAKE<MyClass>();
WHEN_CALLED(fakeMyClass->GetResult()).Return(10);

更多信息,请参阅。

< P>我认为现在不可能用标准C++来做(但我们希望强大的编译时间会很快来到C++……)。然而,这样做有很多选择

你可以看看。现在只是Windows,但计划增加对Linux和Mac的支持

另一个选择是,它似乎与GCC合作,但最近没有任何活动

也提供了这样的功能,但仅限于免费功能。它不支持类成员函数使用它

我不完全确定,但似乎所有这些都是通过在运行时覆盖目标函数来实现的,这样它就可以跳转到伪造的函数

there is是Google Mock的一个扩展,允许您通过重新定义非虚拟函数并依赖于原始函数位于动态库中这一事实来模拟非虚拟函数。它仅限于GNU/Linux平台

最后,你也可以尝试一下(我是作者)

它(目前)不是一个模拟框架,它提供了用测试函数替换生产函数的可能性。我希望能够将其集成到一个或多个模拟框架中;如果没有,它将成为一个。 **更新:*它与FakeIt集成

在链接过程中,它还会覆盖原始函数(因此,如果在定义该函数的同一翻译单元中调用该函数,则该函数将不起作用),但它使用了与C-Mock不同的技巧,因为它使用GNU ld的
--wrap
选项。它还需要对构建系统进行一些更改以进行测试,但不会以任何方式影响主代码(除非您被迫将函数放在单独的.cpp文件中);但提供了将其轻松集成到CMake项目中的支持

但是,目前它仅限于
MyClass* fakeMyClass = FAKE<MyClass>();
WHEN_CALLED(fakeMyClass->GetResult()).Return(10);
class MyClass
{
private:
   int PrivateMethod() { return -1; }
}


MyClass* myClass =  new MyClass();

PRIVATE_WHEN_CALLED(myClass, PrivateMethod).Return(1);