如何应用具有复杂功能的TDD?

时间:2017-07-28 15:02:12

标签: unit-testing testing tdd

Bob叔叔的测试驱动开发的三条规则如下:

  1. 除非要通过失败的单元测试,否则不允许编写任何生产代码。

  2. 您不得再编写任何单元测试,而不能完成失败;并且编译失败就是失败。

  3. 您不能再编写足以通过一次失败单位测试的生产代码。

  4. 在现实世界中,例如,我有一项任务需要完成一个需要一些复杂算法的方法(例如一些高级数学知识),如果我应用上述规则3,它将是这样的:

    • 开始编写测试用例=>运行并看到它失败

    • 编写足以使测试通过的代码行

    • 重构代码

    • 然后重复此循环。

    我的问题是:在这种情况下,这种方法是否非常不现实并且分心?

    为什么我们不编写第一个测试用例,然后专注于找到解决方案然后实现它,我的意思是看大图,而不是编写足够的代码来传递第一个测试用例?

2 个答案:

答案 0 :(得分:3)

我认为另一个答案很好,但除此之外:如果一个方法需要你实现几个算法 - 你确定这样的方法尊重< em>单一责任原则?这听起来像是一种方法做得太多了。

所以,当你退后一步看到使用了3种算法时 - 已经告诉我们每个算法应该进入自己的方法。并且我们的“初始”方法只是调用这些其他方法来完成一些计算。

走向其他方向 - 没有任何法律可以阻止您根据自己的需要适应 TDD。这导致了我称之为“自下而上的TDD”的做法。

它是这样的:而不是第一次为我的一个巨大的方法编写测试 - 我实际上考虑了在这个巨大的方法中我将需要的不同部分。所以我只记下第一部分的测试;然后实现它。我为所有部分都这样做。随着时间的推移,部件会得到增强,也许我会将较小的部件合并为更大的部件(这也可能意味着将多个测试合并为更大的测试)。

这种技术可能意味着可能最终只为你的庞大方法提供一个测试用例 - 但你实际上使用TDD来测试小部件,同时构建“大解决方案”。

换句话说:我没有为那个方法的公共合同编写一个大的功能测试 - 我开始编写我知道我需要的小帮助方法的测试。最后,这些帮助器将是私有方法 - 直接测试它们没有意义。但是你可以保留早期测试的那些部分,这些部分在你的大公共方法的背景下是有意义的。

长话短说:所有这些技巧都是为了指导你找到自己的方式。鉴于你有足够的经验来设计,以这种方式使用TDD是可能的(而且实际上很有趣)!

答案 1 :(得分:2)

根据我对TDD的经验,做这个婴儿步骤有一些好处:

让开发人员考虑如何使用代码

可理解的方法名称,输入(将从文件,数组中读取),输出(将是json,ArrayList)以及每个特定输入的行为(引发异常,不执行任何操作)。

测试反馈更精确

执行婴儿步骤可帮助您专注于如何处理算法中的特定情况。例如,假设使用快速排序算法的TDD:

def "Given an empty list, when I use quicksort, should raise an exception"()
def "Given an list of one elements, should return the list itself"()
def "Given an ordered list of elements, should return the list itself"()
def "Given an unordered list of elements, should return a copy of ordered list"()

请注意,测试本身应该非常精确,并且要详细说明要测试的行为。 一个不太好的测试可能是这样的

def "Given an list, should give me an ordered list"()

如果上面的测试失败,则失败,因为没有抛出异常?或者列表是订购但改变了原始列表?

您的测试代码成为规范

如果您对算法执行了一些广泛的测试代码,您可能会隐藏算法的一些重要细节。所有重要的代码都必须有一个非常好的测试用例。这样做,您将得到一个基本上是您的算法规范的测试代码。 如果其他开发人员询问您的算法应该如何工作,只需向他展示测试用例。

我在开始的时候和你有同样的想法。感觉就像我们在浪费时间,做这些婴儿步骤似乎很荒谬。但是相信我,你练的越多,你就越意识到TDD的好处。