单元测试:对接口进行编码

Unit Testing: coding to interfaces?

本文关键字:编码 接口 单元测试      更新时间:2023-10-16

目前我的项目是由各种具体类组成的。现在,当我进入单元测试,它看起来像我应该为每个类创建一个接口(有效地加倍在我的项目类的数量)?我碰巧正在使用Google Mock作为一个Mock框架。参见谷歌界面模拟食谱。而之前我可能只是类CarEngine,现在我将有抽象类(又名c++接口)CarEngine,然后实现类CarImplementationEngineImpl或其他。这将允许我消除CarEngine的依赖。

在研究这个问题时,我遇到了两种想法:

  1. 只在需要多个接口时使用给定抽象的实现和/或用于公共api;否则,不要创建不必要的接口。

  2. 单元测试存根/模拟通常的"其他实现",所以,是的,你应该创建intefaces .

在进行单元测试时,我应该为项目中的每个类创建一个接口吗?(我倾向于创建易于测试的接口)

我想你有很多选择。正如您所说,一种选择是创建接口。假设你有类

class Engine:
{
public:
    void start(){ };
};
class Car
{
public: 
    void start()
    {
        // do car specific stuff
        e_.start();
private:
    Engine e;
};

要引入接口——你必须把Car改成Engine

class Car
{
public: 
    Car(Engine* engine) :
    e_(engine)
    {}
    void start()
    {
        // do car specific stuff
        e_->start();
private:
    Engine *e_;
};

如果你只有一种类型的引擎-你突然使你的Car对象更难使用(谁创建引擎,谁拥有引擎)。汽车有很多零件,所以这个问题将继续增加。

如果你想要独立的实现,另一种方法是使用模板。这样就不需要接口了。

class Car<type EngineType = Engine>
{
public: 
    void start()
    {
        // do car specific stuff
        e_.start();
private:
    EngineType e;
};

在你的模拟中,你可以用专门的引擎创建汽车:

Car<MockEngine> testEngine;

另一种不同的方法是向Engine添加方法以允许对其进行测试,例如:

class Engine:
{
public:
    void start();
    bool hasStarted() const;
};

然后您可以添加一个检查方法到Car,或者从Car继承到测试。

class TestCar : public Car
{
public:
    bool hasEngineStarted() { return e_.hasStarted(); }
};

这将要求Engine在Car类中从私有更改为受保护。

取决于现实世界的情况,将取决于哪个解决方案是最好的。此外,每个开发人员都有自己的"圣杯",即他们认为代码应该如何进行单元测试。我个人的观点是要牢记客户/顾客。让我们假设您的客户(可能是团队中的其他开发人员)将创建car,而不关心引擎。因此,我不想暴露引擎的概念(我的库内部的一个类),这样我就可以对它进行单元测试。我会选择不创建接口并同时测试这两个类(我给出的第三种选择)。

关于实现可见性有两类测试:黑盒测试和白盒测试

  • 黑盒测试侧重于通过其接口测试实现,并验证对其规范的调整。

  • 白盒测试测试关于不应该从外部访问的实现的细粒度细节。这种类型的测试将验证实现组件是否按预期工作。所以他们的结果主要是开发人员感兴趣的,他们试图找出什么是坏的,或者需要维护

根据

mock的定义,它们适合模块化体系结构,但这并不意味着项目中的所有类都需要完全模块化。当一组班级相互了解时,画一条线是完全可以的。它们作为一个组可以从某个facade接口类的角度呈现给其他模块。但是,您仍然希望在该模块中拥有白盒测试驱动程序,并了解实现细节。因此,这种类型的测试不适合模拟

由此得出的结论是,您不需要为所有东西都提供模拟或接口。只需使用实现facade接口的高级设计组件并为它们创建模拟。它将为您提供最佳位置,模拟测试将获得回报IMHO

已经说过了,试着根据您的需要使用工具,而不是让工具强迫您进行您认为从长远来看不会有益的更改

为项目中的每个类创建接口可能是必要的,也可能不是必要的。这完全是一个设计决策。我发现大多数情况下都不是这样。通常在n层设计中,您希望抽象数据访问和逻辑之间的层。我认为您应该朝着这个方向努力,因为它有助于测试逻辑,而不需要太多测试所需的基础设施。像依赖注入和IoC这样的抽象方法需要您做这样的事情,并使测试所述逻辑变得更容易。

我会检查您正在尝试测试的内容,并将重点放在您认为最容易出错的区域。这可以帮助您决定是否需要接口。