DevOps 是一门软件工程学科,专注于最大限度地缩短实现预期业务影响的提前期。虽然业务利益相关者和赞助商对如何优化业务运营有想法,但这些想法需要在现场进行验证。这意味着业务自动化(即软件产品)必须摆在最终用户和付费客户面前。只有这样,企业才能确认最初的改进想法是否富有成效。
软件工程是一门新兴的学科,交付没有缺陷的产品可能会很困难。因此,DevOps 诉诸于最大限度地提高自动化程度。任何可重复的繁琐工作,例如测试对源代码的已实现更改,都应由 DevOps 工程师自动化。
本文着眼于如何自动化单元测试。这些测试侧重于我喜欢称之为“小型编程”的内容。更重要的测试自动化(所谓的“大型编程”)必须使用不同的学科——集成测试。但那是另一篇文章的主题。
什么是单元?
当我教授单元测试方法时,我的学生常常无法清楚地确定什么是可测试的单元。也就是说,处理的粒度并不总是很清楚。
我想指出,发现有效单元的最简单方法是将其视为行为单元。例如(尽管是一个微不足道的例子),当经过身份验证的客户开始在线购物时,行为单元是一个购物车,其中包含零件商品。一旦我们都同意空购物车包含零件商品,我们就可以专注于自动化单元测试,以确保这样的购物车始终返回零件商品。
什么不是单元?
任何涉及多个行为的处理都不应视为单元。例如,如果购物车处理导致计算购物车中的商品数量,并且计算订单总额,并且计算销售税,并且计算建议的运输方式,则该行为不是单元测试的良好候选对象。这种行为是集成测试的良好候选对象。
何时编写单元测试
关于何时编写单元测试有很多争论。普遍的看法是,一旦代码编写完成,编写自动化脚本来断言已实现的行为单元是否按预期交付功能是一个好主意。这样的单元测试(或几个单元测试)不仅记录了预期的行为,而且所有单元测试的集合确保了未来的更改不会降低质量。如果未来的更改对已实现的行为产生不利影响,则一个或多个单元测试会发出抱怨,这将提醒开发人员发生了回归。
还有另一种看待软件工程的方式。它基于传统的格言“三思而后行”。从这个角度来看,在编写测试之前编写代码相当于切割产品的一部分(例如,椅子腿),并且仅在切割后才测量它。如果进行切割的工匠非常熟练,那么这种方法可能会奏效(有点)。但更有可能的是,以这种方式切割的椅子腿最终会长度不相等。因此,建议在切割前进行测量。这对软件工程实践的意义在于,测量结果以单元测试的形式表达。一旦我们测量了所需的值,我们就会创建一个蓝图(单元测试)。然后,该蓝图用于指导代码的切割。
常识表明,先测量,然后进行切割更为合理。根据这种推理,在编写代码之前编写单元测试是进行正确软件工程的推荐方法。从技术上讲,这种“三思而后行”的方法称为“测试先行”方法。先编写代码的相反方法称为“测试后行”。“测试先行”方法是测试驱动开发 (TDD) 方法论所倡导的方法。稍后编写测试称为测试后开发 (TLD)。
为什么 TLD 有害?
不建议在不测量的情况下进行切割。即使是最有才华的工匠最终也会因不测量而犯错。当我们继续我们的工艺时,缺乏测量最终会追上我们中最有经验的人。因此,最好在切割前制作蓝图(即测量结果)。
但这并不是 TLD 方法被认为有害的唯一原因。当我们编写代码时,我们同时考虑两个不同的问题:代码的预期行为和代码的最佳结构。这两个问题非常不同。这一事实使得妥善完成满足对期望行为和最佳(或至少是体面的)代码结构的期望的任务非常具有挑战性。
TDD 方法通过首先将全部注意力集中在预期的期望行为上来解决这个难题。我们从编写单元测试开始。在该测试中,我们关注我们期望发生什么。此时,我们一点也不关心预期行为将如何实现。
一旦我们完成了描述什么(即,我们期望从我们即将构建的单元中表现出什么行为?),我们就会看到该期望失败。它失败了,因为负责如何实现预期行为的代码尚未实现。现在我们被迫编写负责如何的代码。
在我们编写了负责如何的代码之后,我们运行单元测试并查看我们刚刚编写的代码是否实现了预期的行为。如果实现了,我们就完成了。是时候继续实现下一个期望了。如果没有实现,我们将继续转换代码,直到它成功通过测试。
如果我们选择不进行 TDD,而是先编写代码,然后再编写单元测试,我们就会错过将什么与如何分开的机会。换句话说,我们在编写代码的同时,还要同时处理我们期望代码执行的操作以及如何构建代码以使其正确执行。
因此,在编写代码后编写单元测试被认为是有害的。
5 条评论