C++ 代码整洁之道:C++17 可持续软件开发模式实践 (16):构建安全体系 2.5.2

阅读数:7 2019 年 12 月 4 日 18:48

C++代码整洁之道:C++17可持续软件开发模式实践(16):构建安全体系 2.5.2

(单元测试的命名)

内容简介
本书致力于讲述 C++ 整洁代码之道!如果你想让自己写的代码更加整洁,那么这本书适合你阅读。本书需要熟悉 C++ 语言的基本概念,才能有效的掌握其中的内容。如果你只是想从 C++ 开发开始,并且没有 C++ 语言的基础知识,你应该首先选择一个好的 C++ 入门的练习项目。此外,本书也不包含任何深奥的技巧和杂乱的知识点。我知道 C++ 有很多令人兴奋的技巧,但这些通常不是整洁代码的精神,也不是现代 C++ 的代码风格。除此之外,这本书为了帮助 C++ 程序员提高技能水平,并举例说明如何编写易于理解的、灵活的、可维护的和高效的 C++ 代码。即使你是一个经验丰富的 C++ 开发人员,这本书中也有一些值得学习的地方,我认为这些值得学习的地方能够促进你的工作。书中所提出的原则和实践可以应用于新的软件系统,有时被称为“绿地项目”,以及具有悠久历史的遗留系统,通常被称为“棕地项目”。

如果单元测试失败,开发人员希望立即知道以下信息:

  • 测试单元的名称是什么?谁的单元测试失败了?
  • 单元测试测试了什么?单元测试的环境是怎么样的(测试场景)?
  • 预期的单元测试结果是什么?单元测试失败的实际测试结果又是什么?

因此,单元测试的命名需要具备直观性和描述性,这是非常重要的,我建议建立所有单元测试的命名标准。

首先,以这样的方式命名单元测试模块(依赖于单元测试框架,称为测试用具或测试夹具)是很好的做法,这样单元测试代码很容易衍生于单元测试框架。单元测试应该有一个像 <Unit_under_Test>Test 的名字,很显然,必须用测试对象的名称来替换 <Unit_under_Test> 占位符。例如,如果被测试的系统(SUT)是 Money 单位,与该测试单元对应的单元测试夹具,以及所有的单元测试用例都应该命名为 MoneyTest(见图 2-3)。

C++代码整洁之道:C++17可持续软件开发模式实践(16):构建安全体系 2.5.2

图 2-3 被测试系统(SUT)和单元测试名称

除此之外,单元测试必须有直观的且易理解的名称,如果单元测试的名称或多或少没有意义,比如 testConstructor()、test4391() 或 sumTest(),那么单元测试的名称不会有太大的帮助。通过下面的建议,可以为单元测试取一个好名字。

一般来说,可以在不同场景下使用多种用途的类,一个直观的且易理解的名称应该包含以下三点:

  • 单元测试的前置条件,也就是执行单元测试之前的 SUT 的状态。
  • 被单元测试测试的部分,通常是被测试的过程、函数或方法(API)的名称。
  • 单元测试预期的测试结果。

遵循以上三点建议,测试过程或方法的单元测试命名的模板,如下所示:

复制代码
<PreconditionAndStateOfUnitUnderTest>_<TestedPartOfAPI>_<ExpectedBehavior>

下面是几小段示例代码:

代码 2-1 好的且直观的单元测试命名的示例代码

复制代码
void CustomerCacheTest::cacheIsEmpty_addElement_sizeIsOne();
void CustomerCacheTest::cacheContainsOneElement_removeElement_sizeIsZero();
void ComplexNumberCalculatorTest::givenTwoComplexNumbers_add_Works();
void MoneyTest:: givenTwoMoneyObjectsWithDifferentBalance_theInequalityComparison_Works();
void MoneyTest::createMoneyObjectWithParameter_getBalanceAsString_returnsCorrectString();
void InvoiceTest::invoiceIsReadyForAccounting_getInvoiceDate_returnsToday();

另一个构建直观的且易理解的单元测试名称的方法,就是在单元测试名称中显示特定的需求。这样的单元测试的名称通常能够反应应用程序域的需求,例如,单元测试名称来自于利益相关者的需求。

下面是一些具有特定域需求的单元测试名称的示例:

代码 2-2

复制代码
void UserAccountTest::creatingNewAccountWithExistingEmailAddressThrowsException();
void ChessEngineTest::aPawnCanNotMoveBackwards();
void ChessEngineTest::aCastlingIsNotAllowedIfInvolvedKingHasBeenMovedBefore();
void ChessEngineTest::aCastlingIsNotAllowedIfInvolvedRookHasBeenMovedBefore();
void HeaterControlTest::ifWaterTemperatureIsGreaterThan92DegTurnHeaterOff();
void BookInventoryTest::aBookThatIsInTheInventoryCanBeBorrowedByAuthorizedPeople();
void BookInventoryTest::aBookThatIsAlreadyBorrowedCanNotBeBorrowedTwice();

当你阅读上面这些单元测试的名称时,即使在没有单元测试代码的情况下,也是非常直观的且易理解的。从这些单元测试的名称中可以很容易地得到许多有用的信息。如果单元测试失败,这样的命名将会是一个很大的优势。几乎所有的单元测试框架都会把失败的单元测试的名称输出到标准输出(stdout1),所以,这种直观的且易理解的单元测试命名,极大地促进了错误的定位。

1 默认情况下输出到控制台,当然也有可能重定向到文件、数据库或网络。—译者注

C++代码整洁之道:C++17可持续软件开发模式实践(16):构建安全体系 2.5.2

购书地址 https://item.jd.com/12599914.html?dist=jd

评论

发布