如何对代码进行分解以简化可测试性?

Var*_*una 4 c++ tdd unit-testing

我正在学习单元测试,并想知道如何编写可测试的代码.但是,我不确定如何在不使其复杂的情况下编写可测试代码.我将采用着名的汽车和发动机问题来描述问题.

class Car
{
private:
   Engine m_engine;

public:
   Car();
   // Rest of the car
}
Run Code Online (Sandbox Code Playgroud)

我提出了以下解决方案,以使上述代码可测试.

  1. 更改Car的构造函数以将Engine作为参数.然后模拟引擎并进行测试.但是,如果我没有不同类型的引擎,那么参数化构造函数似乎是不合适的,只是为了使它可测试.

  2. 使用setter然后将模拟引擎传递给setter.与上述相同的流程.

  3. 首先测试引擎,然后使用经过验证的引擎(或使用存根引擎)测试汽车.

我必须在代码上测试哪些替代方案?每种方法的优点和缺点是什么?

Joh*_*ers 7

从不同的(测试驱动开发)观点来看:易于测试的代码易于使用.编写单元测试实际上是在测试代码的"公共接口".如果它很难测试,那是因为你在那里有一些依赖,这使得它很难.你真的需要一个遏制关系,还是一个联想关系会更有意义?

在你的情况下,我个人认为在构造函数中传递Engine会更容易测试,所以我会像你的建议#1那样重构构造函数.您可以在一个测试套件中测试引擎,并提供一个模拟引擎来测试另一个测试套件中的Car.现在测试它很简单,这意味着界面易于使用.这是好事.

现在考虑如何在实际项目中使用该实现.您将创建一个CarFactory类,工厂将创建一个引擎并将其放入Car中,然后再将其交付给您.(还要注意这最终如何更接近地模拟汽车,发动机和工厂的真实世界,但我离题了.)

因此,TDD的答案是重构代码以在构造函数上获取Engine指针.


wis*_*sty 5

如果您只有一个引擎类型,为什么要尝试将其作为新对象?如果您不打算交换引擎,请不要创建另一个抽象层.只需将发动机作为汽车的一部分.

您可能正在分解以降低复杂性,而不是重用组件.好决定.在这种情况下,我会说3是你最好的选择 - 验证你的低级组件,然后使用调用较低级别对象的更高级代码.

实际上,Engine更像是数据库.并且您将需要更改构造函数以使用不同的数据库(出于测试原因或其他原因),但您可以暂时搁置该谎言.

  • 很好的答案,但是以一种意想不到的方式.它表明许多流行的技术都是用于数据库驱动的开发,Web开发或两者兼而有之 - 并且拥护者经常假设db和web是所有软件的. (3认同)