新的unit testing,如何编写出色的testing?

我对unit testing世界相当陌生,而且我刚刚决定在本周为现有的应用程序添加testing覆盖率。

这是一个巨大的任务,主要是因为要testing的类的数量,而且因为写testing对我来说都是新的。

我已经为一堆课程编写了testing,但是现在我想知道我是否正确地做了。

当我为一个方法编写testing时,我有第二次重写我已经写在方法本身的感觉。
我的testing似乎紧紧地绑定到方法上(testing所有的代码path,期望一些内部方法被多次调用,并带有某些参数),似乎如果我重构了方法,testing将会失败,即使该方法的最终行为没有改变。

这只是一种感觉,如前所述,我没有testing经验。 如果一些更有经验的testing人员可以给我如何编写一个现有的应用程序的伟大的testingbuild议,这将不胜感激。

编辑:我很想谢谢堆栈溢出,我有不到15分钟的大量input,回答了我刚才在线阅读的时间。

我的testing似乎紧紧地绑定到方法上(testing所有的代码path,期望一些内部方法被多次调用,并带有某些参数),似乎如果我重构了方法,testing将会失败,即使该方法的最终行为没有改变。

我认为你做错了。

unit testing应该:

  • testing一种方法
  • 为该方法提供一些具体的参数
  • testing结果如预期

它不应该看内部的方法,看看它在做什么,所以改变内部不应该导致testing失败。 你不应该直接testing私人方法被调用。 如果您有兴趣了解您的私人代码是否正在testing,请使用代码覆盖工具。 但是不要被这个迷惑:100%的覆盖率不是必需的。

如果您的方法在其他类中调用公共方法,并且这些调用由您的接口保证,那么您可以使用模拟框架来testing这些调用是否正在进行。

您不应该使用方法本身(或其使用的任何内部代码)来dynamic生成预期的结果。 预期的结果应该被硬编码到你的testing用例中,以便在实现改变时不会改变。 下面是一个unit testing应该做什么的简单例子:

testAdd() { int x = 5; int y = -2; int expectedResult = 3; Calculator calculator = new Calculator(); int actualResult = calculator.Add(x, y); Assert.AreEqual(expectedResult, actualResult); } 

请注意,如何计算结果不会被检查 – 只是结果是正确的。 继续添加更多更简单的testing用例,如上所述,直到您已经覆盖尽可能多的场景。 使用你的代码覆盖工具,看看你是否错过了任何有趣的path。

对于unit testing,我发现testing驱动(testing第一,代码第二)和代码第一,testing第二是非常有用的。

而不是写代码,然后写testing。 编写代码,然后看看你认为代码应该做什么。 想想它的所有用途,然后为每个testing写一个testing。 我发现编写testing比编码本身更快,但涉及更多。 testing应该testing意图。 同时考虑你在testing写作阶段findangular落案例的意图。 当然,在编写testing时,您可能会发现less数使用中的一个会导致一个错误(我经常发现这个错误,而且我非常高兴这个错误没有破坏数据,并且没有经过检查)。

然而testing几乎就像编码两次。 事实上,我有应用程序,有更多的testing代码(数量)比应用程序代码。 一个例子是一个非常复杂的状态机。 我必须确保在添加更多的逻辑之后,整个事情总是在所有以前的用例上起作用。 而且由于这些情况很难通过查看代码来实现,所以我最终为这台机器提供了一个很好的testing套件,我相信在修改之后它不会被破坏,而且testing还能够挽救我的屁股几次。 而当用户或testing人员发现错误的stream量或angular落情况下,猜测什么,添加到testing,再也没有发生过。 除了使整个事情超稳定外,这确实给了用户对我工作的信心。 如果出于性能方面的原因而需要重新编写,那么请猜测,由于testing的结果,所有input都按预期工作。

所有简单的例子如function square(number)都是很好的,而且可能是不好的候选者,要花费大量的时间testing。 那些做重要的商业逻辑,那就是testing重要的地方。 testing要求。 不要只testingpipe道。 如果需求改变,那么猜测是什么,testing也必须。

testing不应该字面上testing该functionfoo调用function栏3次。 那是错的。 检查结果和副作用是否正确,而不是内在的机制。

值得注意的是,对现有代码进行改造的unit testing要比首先通过testing来创build代码困难得多。 这是处理遗留应用程序的大问题之一…如何进行unit testing? 这已经被问过很多次了(所以你可能被closures作为一个愚蠢的问题),人们通常最终在这里:

将现有的代码移动到testing驱动开发

我第二个接受答案的书推荐,但除此之外,有更多的信息在那里的答案。

unit testing是关于从函数/方法/应用程序获得的输出。 结果如何产生并不重要,重要的是它是正确的。 因此,你对内部方法进行计数的方法是错误的。 我倾向于坐下来写一个方法应该返回给定的input值或特定的环境,然后写一个testing,比较返回的实际价值与我想出来的。

不要写testing来全面覆盖你的代码。 写testing,保证你的要求。 您可能会发现不必要的代码path。 相反,如果有必要的话,他们在那里可以满足某种要求; find它是什么,并testing需求(而不是path)。

保持您的testing小:每个要求一个testing。

之后,当您需要更改(或者编写新代码)时,请先尝试编写一个testing。 只有一个。 那么你将在testing驱动开发中迈出第一步。

在编写要testing的方法之前,尝试编写unit testing。

这肯定会迫使你对事情的做法有一点不同的想法。 你不知道这个方法是如何工作的,只是它应该做什么。

你应该总是testing方法的结果,而不是如何得到这些结果。

testing应该提高可维护性。 如果你改变一个方法,而且一个testing可能会是一件好事。 另一方面,如果你把你的方法看作是一个黑盒子,那么这个方法里面什么都不用考虑。 事实是,你需要嘲笑一些testing的东西,在这种情况下,你真的不能把这个方法当作黑匣子。 你唯一能做的就是进行一个集成testing – 你加载一个完全实例化的testing服务实例,让它像在应用中运行一样。 那么你可以把它当作一个黑匣子。

 When I'm writing tests for a method, I have the feeling of rewriting a second time what I already wrote in the method itself. My tests just seems so tightly bound to the method (testing all codepath, expecting some inner methods to be called a number of times, with certain arguments), that it seems that if I ever refactor the method, the tests will fail even if the final behavior of the method did not change. 

这是因为您在编写代码后正在编写testing。 如果你反其道而行(先写testing),它不会有这种感觉。

这是unit testing的最好的书: http : //www.manning.com/osherove/

它解释了所有的最佳实践,做的,而不是unit testing。