你的unit testing有多深?
我发现的关于TDD的事情是,它需要时间来让你的testing设置和自然懒惰我总是想写尽可能less的代码。 我似乎做的第一件事是testing我的构造函数已经设置了所有的属性,但这是矫枉过正?
我的问题是,你写unit testing在什么级别的粒度?
..有没有一个testing太多的情况?
我为代码付出代价,而不是为了testing,所以我的理念是尽可能less的testing来达到一定程度的信心(我怀疑这个级别的信心高于行业标准,但这可能只是自负) 。 如果我通常不会犯一种错误(例如在构造函数中设置错误的variables),我不testing它。 我倾向于理解testing错误,所以当我有复杂的条件逻辑时,我会特别小心。 在团队编码时,我修改我的策略,仔细testing我们总体上出错的代码。
不同的人会根据这个理念有不同的testing策略,但是考虑到testing如何最好地适应编码的内部循环,这对我来说似乎是合理的。 从现在起的一二十年,我们可能会有一个更普遍的理论,写哪些testing,哪些testing不写,以及如何区分差异。 同时,实验似乎是按顺序的。
写unit testing你想要打破的东西,和边缘情况。 之后,应该在bug报告进来时添加testing用例 – 在编写bug修复程序之前。 开发人员可以确信:
- 错误是固定的;
- 该错误不会再出现。
根据所附的评论 – 我想这种编写unit testing的方法可能会导致问题,如果在给定的类中发现许多错误,随着时间的推移。 这可能是判断有用的地方 – 只为可能重新发生的错误添加unit testing,或者重新发生会导致严重问题的unit testing。 我发现在unit testing中进行集成testing可以在这些情况下有所帮助 – testing代码更高的代码path可以覆盖更低的代码path。
一切都应该尽可能简单,但并不简单。 – 爱因斯坦
关于TDD最被误解的事情之一是其中的第一个词。 testing。 这就是BDD出现的原因。 因为人们并不了解第一个D是重要的,即Driven。 我们都倾向于对testing进行一些思考,而对devise的驱动有一点点的关注。 我想这是对你的问题的一个模糊的答案,但你应该考虑如何驱动你的代码,而不是你正在testing的东西; 这是一个覆盖工具可以帮助你的东西。 devise是一个相当大和更有问题的问题。
对于那些提出testing“一切”的人来说:意识到“全面testing”像int square(int x)
这样的方法需要大约40亿个testing用例在常见语言和典型环境中。
事实上,它甚至比这更糟糕:一个方法void setX(int newX)
也不得不改变除了x
以外的任何其他成员的值 – 你testing的obj.y
, obj.z
等等都保持不变在调用obj.setX(42);
?
testing“一切”的一个子集是唯一可行的。 一旦你接受这一点,考虑不testing令人难以置信的基本行为变得更可口。 每个程序员都有一个错误位置的概率分布 ; 聪明的方法是把精力集中在testing区域,在那里你估计bug的概率很高。
经典的答案是“testing任何可能破坏的东西”。 我认为这意味着testing制定者和获取者除了设置或获取之外什么都不做,可能是太多的testing,不需要花时间。 除非你的IDE为你写这些,否则你也可以。
如果你的构造函数没有设置属性可能会导致错误,然后testing,他们设置不是矫枉过正。
我写testing来覆盖我将写的类的假设。 testing强制要求。 基本上,如果x不能是3,例如,我要确保有一个testing覆盖了这个要求。
总之,如果我不写一个testing来覆盖一个条件,它会在“人”testing的时候出现。 那么我肯定会写一个,但我宁愿早点接触他们。 我认为重点是testing是乏味的(可能),但是必要的。 我写足够的testing是完整的,但不超过。
现在跳过简单testing的部分问题是将来重构可能会使得这个简单的属性变得非常复杂,并带有大量的逻辑。 我认为最好的想法是你可以使用testing来validation模块的要求。 如果当你通过X时,你应该回到Y,那么这就是你想要testing的。 然后当你稍后改变代码时,你可以validationX给你Y,你可以添加一个testing给你B,当这个需求稍后添加时。
我发现,在第一次或第二次错误修复时,我在写作testing期间所花费的时间是值得的。 能够拿起你在3个月内没有看过的代码,并且合理地确定你的修补程序覆盖了所有的情况,“可能”不会破坏任何东西都是非常有价值的。 您还会发现unit testing将帮助您分类错误,远远超出堆栈跟踪等。看看应用程序的各个部分是如何工作和失败的,可以让我们深入了解为什么他们工作或失败。
在大多数情况下,我会说,如果有逻辑,testing它。 这包括构造函数和属性,特别是当属性中有多个事物被设置时。
对于太多的testing,这是有争议的。 有人会说,一切都应该进行稳健性testing,其他人说,为了有效的testing,只有可能破坏的东西(即逻辑)应该被testing。
我只是从个人的经验来看,更倾向于第二阵营,但如果有人决定testing一切,我不会说这太过分了……对我来说可能有点矫枉过正,但对他们来说却不算太多。
所以,不 – 我想说的是,没有一般意义上的“太多”testing,只有个人。
testing驱动开发意味着您在所有testing通过时都停止编码。
如果你没有testing一个物业,那么你为什么要实施它? 如果您在“非法”分配的情况下不testing/定义预期的行为,该物业应该做什么?
因此,我完全是为了testing一个class级应该展示的每一个行为。 包括“原始”属性。
为了使这个testing更容易,我创build了一个简单的NUnit TestFixture
,它提供了设置/获取值的扩展点,并获取有效值和无效值的列表,并有一个testing来检查属性是否正确。 testing一个属性可能是这样的:
[TestFixture] public class Test_MyObject_SomeProperty : PropertyTest<int> { private MyObject obj = null; public override void SetUp() { obj = new MyObject(); } public override void TearDown() { obj = null; } public override int Get() { return obj.SomeProperty; } public override Set(int value) { obj.SomeProperty = value; } public override IEnumerable<int> SomeValidValues() { return new List() { 1,3,5,7 }; } public override IEnumerable<int> SomeInvalidValues() { return new List() { 2,4,6 }; } }
使用lambdas和属性,这甚至可以写得更加紧凑。 我收集MBUnit甚至有一些本地支持这样的事情。 问题的关键是,上面的代码捕捉到财产的意图。
PS:也许PropertyTest也应该有一种方法来检查对象的其他属性没有改变。 嗯..回到绘图板。
我进行unit testing以达到最大可行范围。 如果我不能达到一些代码,我会重构,直到覆盖范围尽可能
在完成盲法写作testing之后,我通常会编写一个复制每个错误的testing用例
我习惯于在代码testing和集成testing之间分开。 在集成testing期间(这也是unit testing,但是在组件testing中,所以不是unit testing的结果),我将testing正确执行的要求。
所以我通过编写testing来开发我的程序越多,我就越担心testing的粒度水平。 回想起来,我似乎正在做最简单的事情来实现我的行为validation目标。 这意味着我正在生成一个信任层,我的代码正在做我要求做的事情,但是这不被认为是绝对保证我的代码是无缺陷的。 我觉得正确的平衡是testing标准行为,也许是一个或两个边缘案例,然后转到我devise的下一个部分。
我承认,这不会涵盖所有的错误,并使用其他传统的testing方法来捕捉这些错误。
一般来说,我从小看,input和输出,我知道必须工作。 然后,当我修复错误,我添加更多的testing,以确保我已经修复的东西被testing。 这是有机的,对我来说很好。
你能testing太多吗? 也许吧,但总的来说可能要谨慎一些,尽pipe这取决于应用程序的关键任务。
我认为你必须在你的业务逻辑的“核心”中testing一切。 Getter ans Setter也是因为他们可以接受你可能不想接受的负值或空值。 如果你有时间(总是依靠你的老板),testing其他业务逻辑和调用这些对象的所有控制器(你从unit testing慢慢地进行集成testing)是很好的。
我不会unit testing没有副作用的简单的setter / getter方法。 但是我做了所有其他公开方法的unit testing。 我尝试为我的algorthims中的所有边界条件创buildtesting,并检查我的unit testing的覆盖范围。
它很多工作,但我认为它值得。 我宁愿编写代码(甚至testing代码),而不是通过debugging器中的代码。 我发现代码构build – 部署 – debugging周期非常耗时,而且我已经集成到我的构build中的unit testing更加详尽,我花费更less的时间完成代码构build – 部署 – debugging周期。
你没有说你为什么编码的架构。 但是对于Java,我使用Maven 2 , JUnit , DbUnit , Cobertura和EasyMock 。
我越读越多,我认为一些unit testing就像一些模式:一种语言不足的气味。
当你需要testing你的普通getter实际上是否返回正确的值时,这是因为你可能混合了getter名字和成员variables名字。 inputruby的'attr_reader:name',这不会再发生了。 只是不可能在Java中。
如果你的获得者不平凡,那么你仍然可以为它添加一个testing。
testing让你担心的源代码。
testing你非常有信心的部分代码是没有用的,只要你不犯它的错误。
testing错误修正,以便它是第一次也是最后一次修复错误。
testing以获得不明确的代码部分的信心,以便您创造知识。
在重构和中重构之前进行testing,这样就不会破坏已有的特性。
这个答案更重要的是计算一个给定的方法需要使用多lessunit testing,因为它的关键性/重要性,你知道你想unit testing。 使用McCabe的基础pathtesting技术,您可以做到以下几点,以比简单的“语句覆盖”或“分支覆盖”定量地获得更好的代码覆盖可信度:
- 确定要unit testing的方法的循环复杂度值(例如Visual Studio 2010 Ultimate可以使用静态分析工具为您计算;否则,您可以通过stream图方法手动计算它 – http://users.csc。; calpoly.edu/~jdalbey/206/Lectures/BasisPathTutorial/index.html )
- 列出stream经您的方法的独立path的基本集合 – 请参阅上面的stream程图示例的链接
- 为步骤2中确定的每个独立基础path准备unit testing