kMock 入门版
什么是 kMock?
当您编写原型或测试时,通常依赖它是不可行或不明智的完全真实的物体。 模拟对象实现与真实对象相同的接口 对象(因此它可以用作一个对象),但允许您在运行时指定它将如何使用被使用以及它应该做什么(将调用哪些方法?按什么顺序?如何 很多次?有什么论据?他们会返回什么? ETC)。
术语“假对象”很容易与模拟对象混淆。假货和模仿品 实际上,在测试驱动开发 (TDD) 社区中,含义非常不同:
- 假对象有有效的实现,但通常会采取一些捷径(也许是为了降低操作成本),这使得它们不适合生产。内存中的文件系统就是一个假的例子。
- 模拟是用期望预先编程的对象,它形成了它们期望接收的调用的规范。
如果这一切对您来说似乎太抽象,请不要担心 - 要记住的最重要的事情是模拟允许您检查其自身与使用它的代码之间的交互。
一旦你开始使用模拟,假货和模拟之间的区别就会变得更加清晰。
kMock 是一个用于创建模拟类并使用它们的库(有时我们也称其为“框架”,以使其听起来很酷)。它对 C++ 的作用就像 jMock/EasyMock 对 Java 的作用(嗯,或多或少)。
使用 kMock 时,
- 首先,你使用一些简单的宏来描述你想要模拟的接口,它们将扩展到你的模拟类的实现;
- 接下来,创建一些模拟对象并使用直观的语法指定其期望和行为;
- 然后练习使用模拟对象的代码。一旦出现任何违反期望的情况,kMock 就会发现它。
为什么选择 kMock?
虽然模拟对象可以帮助您消除测试中不必要的依赖关系并使其快速可靠,但在 C++ 中手动使用模拟是困难:
- 必须有人来实现模拟。这项工作通常很乏味且容易出错。难怪人们会避之不及。
- 那些手动编写的模拟的质量有点,呃,不可预测。您可能会看到一些非常精美的内容,但您也可能会看到一些被匆忙修改并具有各种临时限制的内容。
- 您从使用一个模拟中获得的知识不会转移到下一个模拟中。
相比之下,Java 和 Python 程序员有一些优秀的模拟框架(jMock、EasyMock 等),它们可以自动创建模拟。因此,模拟是一种行之有效的技术,并且在这些社区中被广泛采用。拥有正确的工具绝对会带来不同。
kMock 是为了帮助 C++ 程序员而构建的。它受到 jMock 和 EasyMock 的启发,但在设计时考虑了 C++ 的细节。如果以下任何问题困扰您,那就是您的朋友:
- 您陷入了次优设计,希望在为时已晚之前完成更多原型设计,但 C++ 中的原型设计绝不是“快速”的。
- 您的测试速度很慢,因为它们依赖于太多的库或使用昂贵的资源(例如数据库)。
- 您的测试很脆弱,因为他们使用的某些资源不可靠(例如网络)。
- 您想要测试您的代码如何处理故障(例如文件校验和错误),但引起故障并不容易。
- 你需要确保你的模块以正确的方式与其他模块交互,但交互很难观察到;因此,你只能在行动结束时观察副作用,但这充其量也很尴尬。
- 您想要“模拟”您的依赖项,但它们还没有模拟实现;而且,坦率地说,你对其中一些手写的模拟并不感到兴奋。
我们鼓励您使用 kMock 作为
- 一个“设计”工具,因为它可以让您尽早且经常地尝试界面设计。更多的迭代带来更好的设计!
- 一个“测试”工具,用于减少测试的出站依赖性并探测模块与其协作者之间的交互。
快速开始
kMock 与 ktest 捆绑在一起。
一个 Mock Turtles 例子
让我们看一个例子。假设您正在开发一个图形程序依赖于类似 LOGO
用于绘图的 API。您将如何测试它是否做正确的事情?嗯,你可以
运行它并将屏幕与黄金屏幕快照进行比较,但让我们承认这一点:
像这样的测试运行成本昂贵且脆弱(如果您刚刚升级到
闪亮的新显卡具有更好的抗锯齿功能?突然你必须
更新您所有的黄金图像。)。如果你所有的测试都是这样的话那就太痛苦了
像这样。幸运的是,您了解了
依赖注入 并了解正确的事情
要做的:不要让您的应用程序直接与系统 API 对话,而是包装
接口中的 API(例如Turtle)以及该接口的代码:
class Turtle {
...
virtual ~Turtle() {}
virtual void PenUp() = 0;
virtual void PenDown() = 0;
virtual void Forward(int distance) = 0;
virtual void Turn(int degrees) = 0;
virtual void GoTo(int x, int y) = 0;
virtual int GetX() const = 0;
virtual int GetY() const = 0;
};
(请注意,Turtle 的析构函数必须是虚拟的,就像所有您打算继承的类 - 否则是析构函数
通过基类删除对象时,不会调用派生类指针,并且您将得到损坏的程序状态,例如内存泄漏。)
您可以使用PenUp()控制海龟的移动是否留下痕迹
和PenDown(),并使用Forward()、Turn()和控制其移动
GoTo()。最后,GetX()和GetY()告诉你当前的位置
龟。
您的程序通常会使用此接口的实际实现。在测试中,您可以使用模拟实现代替。这使您可以轻松地 检查您的程序正在调用什么绘图基元,使用什么参数,以及 按什么顺序。以这种方式编写的测试更加健壮(它们不会破坏 因为你的新机器的抗锯齿处理方式不同),更容易阅读和 维护(测试的意图在代码中表达,而不是在某些二进制文件中表达) 图像),并且运行快得多。
编写模拟类
如果幸运的话,您需要使用的模拟已经由 一些好人。但是,如果您发现自己可以编写模拟 上课,放松 - kMock 将这个任务变成了一个有趣的游戏! (嗯,差不多了。)
如何定义它
以Turtle界面为例,以下是您需要的简单步骤
跟随:
- 从
Turtle