我认为我的项目已解耦到足以进行单元测试的程度。但是,就笔迹和功能而言,我的项目到底需要多大才能使单元测试值得?
我们都会犯错,没有人能做到完美,但是我认为自己是一个体面的程序员,可以逐步解决小项目的错误。还是您的项目规模不限,都必须进行单元测试?
我认为我的项目已解耦到足以进行单元测试的程度。但是,就笔迹和功能而言,我的项目到底需要多大才能使单元测试值得?
我们都会犯错,没有人能做到完美,但是我认为自己是一个体面的程序员,可以逐步解决小项目的错误。还是您的项目规模不限,都必须进行单元测试?
Answers:
以我的经验,一个类和一个功能足以考虑进行单元测试的必要性。
class Simple {
boolean reallySimple() {
return true; // how do we make sure it doesn't change to false?
}
}
class SimpleTest {
void assertReallySimple() {
// ensure reallySimple return value isn't changed unexpectedly
UnitTestFramework.assertTrue(new Simple().reallySimple());
}
}
我从来没有接受过“必须对所有内容进行单元测试”的想法,尽管肯定有一些人(请参阅gnat的答案!)。
就我而言,单元测试的主要好处是:
基本上,您需要权衡这些因素所花费的时间来编写和维护测试。
1号通常足以使其值得编写测试。根据我的经验,迟早会有超过95%的代码被修改。
很简单:如果您在运行一次程序后将其丢弃,则无需进行单元测试。
如果您觉得这太过分了,请考虑替代方案的含义。如果某个大小低于该大小而单元测试不付钱,则您必须牢记:“我是否达到了不可思议的大小?我应该开始编写测试了吗?” 现在,众所周知,程序员在预测未来方面很糟糕,并且在判断自己的技能方面也非常糟糕。现在,即使等待一个月,对您而言仍然清晰的代码将变得难以理解。你是绝对的一个项目,积极某些永远不会被再次利用,而你保持周围只是对你可能想查找你是如何解决的东西之前,远程机会将再次请求,并会收到变更请求。
因此,很可能会误判测试是否有帮助,并且当您开始需要测试时,您已经不确定100%实际要测试的语义是什么。由于我认为除琐碎的一次性程序外,所有其他程序都可以从单元测试中受益,因此,我认为,对从未进行过测试的测试进行扩展所付出的成本对于扩展未经测试和无法理解的代码的风险可以忽略不计。
前一段时间,我发现了一篇不错的文章:为什么单元测试可以加快开发速度。它可以帮助您回答问题。
...如果代码库...提供了大量的单元测试该怎么办?一组会说:“如果所有测试成功,我保证代码仍会执行应做的事情”,如果测试失败,它将准确地向您显示某些行为被破坏的地方。太好了,我可以更改代码以添加我想要的内容,而无需徘徊,如果代码仍然可以执行其应做的事情,那么我只需运行...测试即可,并且有信心。作为软件工程师,这是一个伟大的世界。我注意到我可以更快地前进...
我有信仰”。
这可能是单元测试加速企业环境中开发的最重要原因。
我可以相信,如果所有测试通过,一切仍然可以进行。如果测试失败,他们将指出问题的根源。
...您必须具有较高的单元测试覆盖率和良好的单元测试。在遵守这些条件的情况下,您会发现自己在添加新功能上所做的努力几乎与应用程序的大小无关,并且从长远来看将加速开发。
迈克尔·费瑟斯(Michael Feathers)在他的一本书中介绍了两种处理代码更改的方法:
代码库有多大无关紧要。
根据Kent Beck在他对Stack Overflow问题的回答中,您的单元测试有多深?,您应该测试容易出错的代码。
如果我通常不会犯某种错误(例如在构造函数中设置错误的变量),则无需进行测试。
实施单元测试可以节省时间并改善:
必须为程序的相对复杂的部分编写单元测试。
如果确定现在编写单元测试不会节省将来的时间,则可以跳过它。但是,您永远不会知道,就我个人而言,我不认为不编写单元测试而是手动进行所有测试会更便宜(在时间,金钱和神经方面)。
通常,可以通过回答以下问题来回答“我应该对此进行单元测试”:“此功能正常工作是否重要,并且知道何时停止工作对我来说很重要?”
当然,这要复杂得多,但这是一个很好的起点。最终,您还将权衡代码是否已经通过在另一个函数中使用而进行了测试,或者您的时间是否会花费在另一个函数上等,等等。
除非您要编写未经测试的代码,否则总会招致测试成本。
有单元测试和没有单元测试之间的区别是编写测试的成本和运行测试的成本与手工测试的成本之间的差异。
如果编写单元测试的成本为2分钟,而运行单元测试的成本实际上为0,但是手动测试代码的成本为1分钟,那么在两次运行测试时您将收支平衡。
多年以来,我一直误以为我没有足够的时间为我的代码编写单元测试。当我确实编写测试时,它们是肿的,沉重的东西,这仅鼓励我认为我应该只在知道需要它们时才编写单元测试。
最近,我被鼓励使用“ 测试驱动开发”,我发现它是一个完整的启示。我现在坚信,我没有时间不编写单元测试。
根据我的经验,通过考虑测试进行开发,最终会得到更简洁的界面,更集中的类和模块以及通常更多的SOLID可测试代码。
每次我使用没有单元测试的旧代码,而不得不手动测试某些东西时,我一直在想:“如果此代码已经具有单元测试,这将更快。” 每当我不得不尝试向具有高耦合性的代码中添加单元测试功能时,我总是在想:“如果以非耦合的方式编写,这将变得更加容易”。
TL; DR版本:
当编写测试的成本加上需要运行多次的成本时,编写测试的成本可能会比手动测试所需的多次成本低。
请记住,尽管使用TDD,但随着测试的提高,编写测试的成本可能会降低,并且除非代码绝对琐碎,否则最终运行测试的频率可能会超出预期。
因为你一旦测试观察回归发生或恐惧造成了一些与你的编辑和没有注意到。
Kindle感到恐惧,让它增长到适当的大小:测试越早越好。
请注意:根据您在项目中的角色,单元测试可能不是您要编写的唯一测试类型。
过去,由于主流测试实践不当,每个人都应该沉迷于单元测试。但是,如果您以前从未进行过测试,那么您真的应该将重点放在一般的测试上,仅凭单元测试并不能解决世界上的问题。
我认为,决定项目是否应使用测试的不是项目的大小,而是项目的类型。
如果您正在研究概念验证,或者正在以从编码中学习为目标的其他项目中,则不需要进行测试。如果要使用该项目,甚至将其投入生产,则应进行测试。
罗伯特·C·马丁(Robert C. Martin)的“清洁代码”中的一句名言:“如果编写起来很简单,那么测试就很简单”,因此没有理由跳过简短而琐碎的程序的测试。
实际上,这与大小无关,它是您要使用的功能(以及它的年龄)。如果我想学习某项技术的工作原理并计划将其扔掉(我们称这是Scrum的峰值),那么我将在不做大量测试的情况下进行编码。如果您打算进一步开发它(即使只是闲逛),则应该围绕代码编写测试。TDD甚至在测试技术之前就已经是一种设计技术。您需要清楚地了解想要执行的代码,然后再确定执行的细节。我也不会建议尝试围绕大量的旧代码编写大量的单元测试。尝试确定那些复杂的部分(具有很高的圈复杂度/意大利面条式的感觉)和那些经常失败的部分(它们通常是相同的)。正如其他人所建议的那样,我会去阅读肯特·贝克(Kent Beck)关于TDD的书,并且一定会读迈克尔·费瑟(Michael Feather)的书。它们没有涉及的太多是围绕代码编写单元测试的政治方面。许多开发人员讨厌不得不更改其代码以使其可测试,并且许多开发人员根本不认为需要测试代码。这是我们必须从事的职业。其他所有工程学科都必须证明(通常是数学上)他们的工作符合规范。我们应该做同样的事情。
在类或功能的限制中绑定项目,然后判断是否适合单元测试可能是错误的。对应用程序进行单元测试有许多好处,但是当您必须维护应用程序或必须在分布式环境中工作时,真正的美就开始了。所以选择来了。即使您的代码进行很小的更改也会造成灾难。
我不仅会建议“单元测试”,而且还会建议检查您的代码覆盖率,以确保单元测试覆盖了最大数量的代码。代码覆盖率的阈值不得小于95%,目标值必须在100%左右。您可以使用工具来跟踪代码覆盖范围并生成报告。
Visual Studio提供覆盖率数据-http://msdn.microsoft.com/zh-cn/library/ms182496(v=vs.80).aspx
也可以使用NCover或dotCover。