TDD方法可以自上而下应用吗?


13

我不清楚TDD(该方法论)如何处理以下情况。假设我想在Python中实现mergesort算法。我首先写

assert mergesort([]) === []

并且测试失败

NameError:未定义名称“ mergesort”

然后我添加

def mergesort(a):
    return []

我的测试通过了。接下来我添加

assert mergesort[5] == 5

我的测试失败了

断言错误

我通过

def mergesort(a):
    if not a:
        return []
    else:
        return a

接下来,我添加

assert mergesort([10, 30, 20]) == [10, 20, 30]

现在我必须尝试通过此步骤。我“知道” mergesort算法,所以我这样写:

def mergesort(a):
    if not a:
        return []
    else:
        left, right = a[:len(a)//2], a[len(a)//2:]
        return merge(mergesort(left)), mergesort(right))

这失败了

NameError:未定义名称“合并”

现在是问题。如何开始merge使用TDD 并开始实施?看来我做不到,因为我没有完成“挂起”的测试mergesort未通过,直到merge完成,测试才能通过如果此测试仍然存在,我将永远无法真正做到TDD,因为在TDD迭代构建过程中我不会变得“绿色” merge

似乎我陷入了以下三个丑陋的场景,想知道(1)TDD社区更喜欢其中哪一个,或者(2)我还缺少另一种方法?我看过鲍勃叔叔TDD演练的几本,并且不记得以前看到过这样的情况!

这是3种情况:

  1. 在具有不同测试套件的不同目录中实现合并。
  2. 开发辅助功能时,不必担心绿色,只需手动跟踪您真正要通过的测试即可。
  3. 注释掉(GASP!)或删除该mergesort调用中的行merge;然后开始merge工作后,将它们放回去。

这些对我来说都是愚蠢的(或者我看错了吗?)。有谁知道首选方法?


2
TDD的目标之一是帮助您创建软件设计。该设计过程的一部分是发现产生所需结果所需的内容。在的情况下mergesort,由于它已经是一个定义非常明确的算法,因此不需要此发现过程,然后就需要将您已经知道的设计映射到一系列单元测试。大概是,您的顶级测试断言您所测试的方法接受未排序的集合并返回已排序的集合……
Robert Harvey

1
...后续的单元测试将逐渐深入研究a的实际机制mergesort。如果您正在寻找实现此目标的“正确”方法,除了准确地将mergesort算法映射到一系列单元测试之外,没有其他方法了。即,他们应该反映mergesort实际情况。
罗伯特·哈维,

4
设计不能仅仅依靠单元测试来发展。如果您期望mergesort设计从红绿重构中自然而然地出现,那么除非您根据现有的知识来指导流程,否则不会发生这种情况mergesort
罗伯特·哈维


1
在TDD中,merge必须仅在“重构”阶段才发明。如果您看到merge可以通过该方法来通过mergesort您的测试,请首先使您的测试通过而不使用merge方法。然后通过介绍merge方法重构您的实现。
法比奥

Answers:


13

这是查看您的选择的一些替代方法。但是首先,TDB的规则,来自鲍勃叔叔,我特别强调:

  1. 除非要通过失败的单元测试,否则不允许编写任何生产代码。
  2. 不允许编写任何足以导致失败的单元测试。编译失败就是失败。
  3. 您不能编写任何足以通过一项失败的单元测试的生产代码

因此,读取规则编号3的一种方法是,您需要merge通过测试的功能,因此可以实现它-但只能以其最基本的形式实现。

或者,也可以从内联编写合并操作开始,然后在使测试生效后将其重构为函数。

另一种解释是,您正在编写mergesort,知道您需要一个merge操作(即,不是YAGNI,这是“足够”的规则试图减少的操作)。因此,您应该从合并测试开始,然后再进行整体排序测试。


这些都是非常好的观察。我之前曾考虑过内联和分解,但是merge令人惊讶的是,它是乱七八糟的,边沿大小写(以及作为独立的有用),将其作为单独的函数来做的想法更有意义。但是,以基本形式内联然后在“蓝帽子”阶段将其排除在外的处理方式似乎确实是正确的,而且非常符合我的期望。
雷·托尔

@RayToal-我实际上倾向于merge在进行排序之前对操作进行全面测试(以及对partition操作进行单独测试)。我认为所声称的好处紧急设计来自缓慢地朝着一个已知的目标努力。在mergesort的情况下,我认为目标不是一般的排序(因为这样一来,您最终将得到冒泡排序)。您了解基本的操作,因此您会朝着这些操作努力;排序主要是事后的想法。
kdgregory 2016年

1
有第四种选择。将merge函数传递mergesort并模拟其行为。然后返回并merge首先执行测试。代表们真棒™。
RubberDuck

@RubberDuck模拟核心域的组成部分可能会导致一些麻烦,更具体地说,当您运行程序并且嘲笑和合并功能的细节差别最小时。对于您正在使用外部资源的实例(例如来自要排序的列表的来源)的实例,应该保留这样的方法。
cllamach
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.