【问题标题】:How to write unit test first and code later?如何先写单元测试,后写代码?
【发布时间】:2013-09-25 11:43:42
【问题描述】:

我是单元测试的新手,并且已经阅读了好几遍我们应该先编写单元测试然后再编写实际代码。到目前为止,我正在编写我的方法,然后对代码进行单元测试。

如果您先编写测试...

您倾向于编写代码以适应测试。这鼓励了 “解决问题的最简单的事情”类型的开发和保持 你专注于解决问题而不是解决元问题。

如果你先写代码……

您会很想编写适合代码的测试。实际上这 相当于编写问题以适合您的答案,即 有点倒退,并且经常会导致测试 价值较低。

对我来说听起来不错。但是,即使在我的代码到位之前,我如何编写单元测试? 我是按字面意思接受建议吗?这是否意味着我应该准备好我的 POCO 类和接口,然后编写单元测试?

谁能用一个简单的例子来解释我是如何做到的,比如两个数字相加?

【问题讨论】:

  • 编写测试首先会迫使您考虑一个类/方法/算法应该在真正实现它之前做什么。这就像说“是我想要发生的事情!我会担心以后的如何。”
  • 要考虑的一件事是,当您已经有了针对特定组件的设计时,TDD 是很好的选择。这通常发生在原型之后。如果您正在设计原型或进行 RAD,TDD 将成为障碍。
  • @lazyberezovsky 很好的例子。谢谢。
  • 我强烈建议,一旦你掌握了它的基础知识,我的意思是可能需要一周的练习,你就可以查看 Visual Studio 的 NCrunch add-on。 Mono 也有另一种选择,但它会在您编写测试时在后台运行您的测试。试试这个演示,我想你会被迷住的。我与他们没有任何关系,但没有它我永远不会编码。

标签: c# unit-testing nunit moq


【解决方案1】:

真的很简单。红色、绿色、重构。

红色表示 - 您的代码已完全损坏。语法高亮显示为红色,测试未通过。为什么?你还没有写任何代码。

绿色表示 - 您的应用程序已构建并且测试通过。您已添加所需的代码。

重构意味着 - 清理它并确保测试通过。

你可以先写一个类似这样的测试:

[TestMethod]
public void Can_Create_MathClass() {
    var math = new MathClass();
    Assert.IsNotNull(math);
}

这将失败(RED)。你如何解决它?创建类。

public class MathClass {
}

就是这样。它现在通过(GREEN)。下一个测试:

[TestMethod]
public void Can_Add_Two_Numbers() {
    var math = new MathClass();
    var result = math.Add(1, 2);
    Assert.AreEqual(3, result);
}

这也失败了(RED)。创建Add 方法:

public class MathClass {
    public int Add(int a, int b) {
        return a + b;
    }
}

运行测试。这将通过(GREEN)。

重构是清理代码的问题。这也意味着您可以删除多余的测试。我们知道我们现在有MathClass.. 所以你可以完全删除Can_Create_MathClass 测试。完成后..您已通过 REFACTOR,可以继续。

请务必记住,重构步骤不仅仅意味着您的普通代码。这也意味着测试。你不能让你的测试随着时间的推移而恶化。您必须将它们包含在重构步骤中。

【讨论】:

  • 通过Can_Add_Two_Numbers的中间步骤,Add方法可以只是return 3;。在这种微不足道的情况下,这是不必要的。但在稍微复杂一些的情况下,看起来很明显的事情对其他人和/或多年后可能并不那么明显。
  • 我同意,但在实践中我发现这一步完全取决于被测试模型的复杂性。很少有它如此复杂以至于我采取了缓冲步骤。
  • 谢谢西蒙。这有帮助。
  • 迄今为止我遇到的最佳解释。非常感谢!
【解决方案2】:

当您在代码之前先创建测试时,您会发现创建代码更加容易和快捷。创建单元测试并创建一些代码以使其通过所需的总时间与直接编写代码所花费的时间大致相同。但是,如果您已经拥有单元测试,则无需在代码之后创建它们,从而节省您现在和以后的很多时间。

创建单元测试有助于开发人员真正考虑需要做什么。需求通过测试牢固地确定下来。以可执行代码的形式编写的规范不会有任何误解。

您将创建的代码简单明了,只实现您想要的功能。其他开发人员可以通过浏览测试了解如何使用这个新代码。结果未定义的输入将明显不在测试套件中

系统设计也有好处。对某些软件系统进行单元测试通常非常困难。这些系统通常首先构建代码,然后再进行测试,通常完全由不同的团队完成。通过首先创建测试,您的设计将受到测试对客户有价值的一切的愿望的影响。您的设计将通过更容易测试来反映这一点。

【讨论】:

    【解决方案3】:

    让我们举一个稍微高级一点的例子:你想写一个从序列中返回最大数的方法。

    首先,为要测试的方法编写一个或多个单元测试:

    int[] testSeq1 = {1, 4, 8, 120, 34, 56, -1, 3, -13};
    
    Assert.That(MaxOf(testSeq1) == 120);
    

    然后重复一些更多的序列。还包括一个空参数、一个具有一个元素的序列和一个空序列,并决定一个空序列或空参数是否应该引发异常(如果是这种情况,请确保单元测试预期空序列的异常)。

    在此过程中,您需要确定方法的名称及其参数的类型。

    此时,它不会编译。

    然后为方法写一个存根:

    public int MaxOf(IEnumerable<int> sequence)
    {
        return 0;
    }
    

    此时它可以编译,但单元测试失败。

    然后实现MaxOf() 以便这些单元测试现在通过。

    这样做可以确保您立即关注该方法的可用性,因为您尝试做的第一件事就是使用它 - 甚至在开始编写它之前。此时您可能会决定根据使用模式稍微更改方法的声明。

    现实世界的示例会将这种方法应用于使用整个类,而不仅仅是一种方法。为了简洁起见,我省略了上面示例中的类。

    【讨论】:

      【解决方案4】:

      可以在编写任何代码之前编写单元测试 - Visual Studio 确实具有从您在单元测试中编写的代码生成方法存根的功能。这样做也可以帮助理解对象需要支持的方法——有时这可以帮助以后的增强(如果你有一个保存到磁盘,你也可以重载保存到Stream,这更可测试并且如果以后需要,有助于通过网络进行假脱机)

      【讨论】:

        猜你喜欢
        • 2013-06-02
        • 2020-06-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-02-12
        • 2015-03-30
        • 2012-02-03
        相关资源
        最近更新 更多