目前我的项目由各种具体课程组成。现在,当我进入单元测试时,看起来我应该为每个类创建一个接口(有效地将项目中的类数量加倍)?我碰巧使用Google Mock作为模拟框架。见Google Mock CookBook on Interfaces。在我可能只有类Car
和Engine
之前,现在我将有抽象类(又名C ++接口)Car
和Engine
,然后是实现类{{1} }和CarImplementation
或其他什么。这样我就可以隐藏EngineImpl
对Car
的依赖。
我在研究这个问题时遇到了两条思路:
只有在需要多个接口时才使用接口 实现给定的抽象和/或在公共API中使用, 所以否则不要不必要地创建接口。
单元测试存根/模拟 通常是“其他实现”,所以,是的,你应该创建 intefaces。
进行单元测试时,我应该为项目中的每个类创建一个接口吗? (我倾向于创建易于测试的界面)
答案 0 :(得分:5)
认为你有很多选择。如你所说,一种选择是创建接口。假设你有课程
class Engine:
{
public:
void start(){ };
};
class Car
{
public:
void start()
{
// do car specific stuff
e_.start();
private:
Engine e;
};
要介绍接口 - 您必须更改Car才能使用引擎
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添加一个check方法,或者从Car继承来测试。
class TestCar : public Car
{
public:
bool hasEngineStarted() { return e_.hasStarted(); }
};
这将要求在Car类中将Engine从private更改为protected。
视现实情况而定,取决于哪种解决方案最佳。此外,每个开发人员都有自己的圣杯,他们如何相信代码应该进行单元测试。我个人的观点是牢记客户/客户。让我们假设您的客户(可能是您团队中的其他开发人员)将创建汽车并且不关心引擎。因此,我不想公开Engines(我的库内部的类)的概念,因此我可以对事物进行单元测试。我会选择不创建接口并一起测试这两个类(我给出的第三个选项)。
答案 1 :(得分:1)
有两类关于实施可见性的测试:黑盒测试和白盒测试
黑盒测试侧重于通过其接口测试实现,并验证对其规范的调整。
白盒测试测试通常可以从外部访问不应该的实现的详细细节。此类测试将验证实现组件是否按预期工作。所以他们的结果大多是开发人员想要弄清楚什么是破碎或需要维护的兴趣
按照他们的定义模拟适合模块化体系结构,但并不是说项目中的所有类都需要完全模块化。当一组课程相互了解时,它可以画出一些线条。它们作为一个组可以从一些外观接口类的角度呈现给其他模块。但是,您仍然希望在此模块中具有白盒测试驱动程序,并了解实现细节。因此,这种测试不适合模拟。
由此可以简单地说,你不需要为所有东西都有模拟或接口。只需采用实现外观界面的高级设计组件并为它们创建模拟。它会给你一个甜蜜点,模拟测试得到回报恕我直言
话虽如此,尝试使用该工具满足您的需求,而不是让工具强制您进行改变,您认为从长远来看不会有益
答案 2 :(得分:0)
为项目中的每个类创建接口可能需要也可能不需要。这完全是一个设计决定。我发现它主要不是。通常在n-tier设计中,您希望在数据访问和逻辑之间抽象层。我认为你应该努力做到这一点,因为它有助于测试逻辑,而没有测试所需的太多基础设施。像dependency injection and IoC这样的抽象方法需要你做这样的事情,并且可以更容易地测试所述逻辑。
我会检查您要测试的内容并关注您认为最容易出错的区域。这可以帮助您确定是否需要接口。