gpt4 book ai didi

c++ - 如何在没有模拟框架的情况下对具有讨厌依赖关系的类进行单元测试?

转载 作者:塔克拉玛干 更新时间:2023-11-02 23:21:51 26 4
gpt4 key购买 nike

我在遗留 C++ 代码库中工作,我想在一个类 DependsOnUgly 上测试一些方法,它具有在大类上不容易破坏的依赖性 (Ugly) 对文件系统等有很多外部依赖性。我想至少获得一些 DependsOnUgly 被测试的方法,同时尽可能少地修改现有代码。如果不进行大量代码修改,就无法通过工厂方法、方法参数或构造函数参数来创建接缝; Ugly 是一个没有任何抽象基类的直接依赖的具体类,并且有大量方法,很少或没有标记为 virtual,完全模拟这将是非常乏味的。我没有可用的模拟框架,但我想对 DependsOnUgly 进行测试,以便进行更改。我怎样才能打破 Ugly 的外部依赖关系来对 DependsOnUgly 上的方法进行单元测试?

最佳答案

使用我所说的预处理器模拟——通过预处理器接缝注入(inject)的模拟。

我首先在 this question 中发布了这个概念在 Programmers.SE 上,根据答案我判断这不是一个众所周知的模式,所以我认为我应该分享它。我很难相信以前没有人做过这样的事情,但因为我找不到它的文档,所以我想我会与社区分享它。

为了示例,这里是 UglyNotAsUgly 的概念性实现。

DependsOnUgly.hpp

#ifndef _DEPENDS_ON_UGLY_HPP_
#define _DEPENDS_ON_UGLY_HPP_
#include <string>
#include "Ugly.hpp"
class DependsOnUgly {
public:
std::string getDescription() {
return "Depends on " + Ugly().getName();
}
};
#endif

丑陋的.hpp

#ifndef _UGLY_HPP_
#define _UGLY_HPP_
struct Ugly {
double a, b, ..., z;
void extraneousFunction { ... }
std::string getName() { return "Ugly"; }
};
#endif

有两种基本变体。第一个是 DependsOnUgly 只调用 Ugly 的某些方法,而您已经想模拟这些方法。第二个是

技术 1:替换 DependsOnUgly 使用的所有 Ugly 行为

我将此技术称为预处理器部分模拟,因为模拟仅实现被模拟类接口(interface)的必要部分。在 mock 类的头文件中使用与生产类同名的 include guard 来导致生产类永远不会被定义,而是 mock。请务必在 DependsOnUgly.hpp 之前包含模拟。

(请注意,我的测试文件示例不是 self 验证的;这只是为了简单起见,并且与单元测试框架无关。重点是文件顶部的指令,而不是实际的指令测试方法本身。)

测试.cpp

#include <iostream>
#include "NotAsUgly.hpp"
#include "DependsOnUgly.hpp"
int main() {
std::cout << DependsOnUgly().getDescription() << std::endl;
}

NotAsUgly.hpp

#ifndef _UGLY_HPP_ // Same name as in Ugly.hpp---deliberately!
#define _UGLY_HPP_
struct Ugly { // Once again, duplicate name is deliberate
std::string getName() { return "not as ugly"; } // All that DependsOnUgly depends on
};
#endif

技巧2:替换DependsOnUgly使用的Ugly的部分行为

我称其为就地子类模拟,因为在这种情况下,Ugly 被子类化,必要的方法被覆盖,而其他方法仍然可用——但是子类的名称仍然是 Ugly。定义指令用于将 Ugly 重命名为 BaseUgly;然后使用 undefine 指令,模拟 Ugly 子类 BaseUgly。请注意,这可能需要根据具体情况将 Ugly 中的某些内容标记为虚拟。

测试.cpp

#include <iostream>
#define Ugly BaseUgly
#include "Ugly.hpp"
#undef Ugly
#include "NotAsUgly.hpp"
#include "DependsOnUgly.hpp"
int main() {
std::cout << DependsOnUgly().getDescription() << std::endl;
}

NotAsUgly.hpp

#ifndef _UGLY_HPP_ // Same name as in Ugly.hpp---deliberately!
#define _UGLY_HPP_
struct Ugly: public BaseUgly { // Once again, duplicate name is deliberate
std::string getName() { return "not as ugly"; }
};
#endif

请注意,这两种方法都不太可靠,应谨慎使用。随着更多的代码库正在测试中,它们应该被移走,并在可能的情况下用更标准的方法来打破依赖关系。请注意,如果遗留代码库的 include 指令足够困惑,它们都可能变得无效。但是,我已经成功地将它们用于实际的遗留系统,所以我知道它们可以工作。

关于c++ - 如何在没有模拟框架的情况下对具有讨厌依赖关系的类进行单元测试?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16023539/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com