《C++代码整洁之道:C++17 可持续软件开发模式实践》 —2.5.10 不要混淆测试代码和产品代码
2.5.10 不要混淆测试代码和产品代码
有时开发人员产生了一个想法,用测试代码来装备他们的生产代码。例如,在测试期间,一个类可能以如下方式包含了处理协作类的依赖关系的代码:
代码2-6 在测试过程中处理依赖关系的一种可能的解决方案
DataAccessObject是特定DAO(数据访问对象)的抽象基类,在本例中为CustomerDAO和FakeDAOForTest,后者就是所谓的测试替身(fake object),这是一个用于测试的虚拟对象(参见本章后面的2.5.12节),目的是替换真正的DAO,因为我们不想测试它,并且我们不想在测试期间保存Customer的数据(谨记我关于数据库的建议)。使用两个DAO中的哪一个由布尔数据成员inTestMode控制。
这段代码虽然可行,但这一解决方案有几个缺点。
首先,我们的生产代码会混杂测试代码,虽然初看并不显眼,但它会增加产品复杂度并降低代码的可读性。我们需要一个额外的成员来区分系统的测试模式和生产使用,这个布尔成员与客户无关,更不用说系统的域了。而且不难想象系统中的许多类都需要这种类型的成员。
此外,Customer类依赖于CustomerDAO和FakeDAOForTest,你可以在源代码头部的包含文件列表中看到它,这意味着在生产环境中测试虚拟类FakeDAOForTest也是系统的一部分,我们寄希望于测试替身的代码永远不会在生产中被调用,但是它确实被编译、链接并部署在了生产中。
当然,也有一些更优雅的方法来处理这些依赖关系,并保证生产代码不受测试代码的影响。例如,我们可以在Customer::save()中注入特定的DAO作为一个参考参数。
代码2-7 避免依赖测试代码(1)
或者,也可以在构造Customer类型的实例期间完成。在这种情况下,我们必须将DAO的一个引用作为类的成员属性。此外,我们必须通过编译器禁止自动生成默认构造函数,因为我们不希望Customer的任何用户可以创建一个未正确初始化的实例。
代码2-8 避免依赖测试代码(2)
deleted函数[C++11]
在C++中,如果有些类型成员没有被定义,编译器会自动为这些类型生成所谓的特殊成员函数(默认构造函数、拷贝构造函数、拷贝赋值运算符和析构函数)。从C++11开始,这个特殊成员函数列表多了移动构造函数和移动赋值运算符。C++11(及更高版本)提供了一种简单且声明性的方法来阻止自动创建任何特殊成员函数、普通成员函数和非成员函数,你可以删除它们。例如,你可以通过以下方式阻止创建默认构造函数:
另一个例子:你可以删除new运算符以防止在堆上动态分配一个类:
第三种替代方案是特定的DAO可以由Customer类已知的一个工厂(请参阅第9章中有关设计模式的Factory模式部分)来创建。如果系统在测试环境中运行,我们可以从外部配置Factory以创建所需的DAO。无论你选择哪种可能的解决方案,Customer类都能与测试代码脱离,Customer与特定的DAO没有依赖关系。
- 点赞
- 收藏
- 关注作者
评论(0)