Unit testing 如何应用功能复杂的TDD?

Unit testing 如何应用功能复杂的TDD?,unit-testing,testing,tdd,Unit Testing,Testing,Tdd,Bob叔叔关于测试驱动开发的三条规则如下: 您不允许编写任何生产代码,除非它是为了通过失败的单元测试 不允许您编写任何足以导致失败的单元测试;编译失败就是失败 不允许您编写的生产代码超过足以通过一个失败的单元测试的数量 例如,在现实世界中,我的任务是完成一个需要一些复杂算法和一些高级数学知识的方法。例如,如果我应用上述3条规则,它将是这样的: 开始编写一个测试用例=>run,并查看它是否失败 编写足够使测试通过的代码行 重构代码 然后重复这个循环 我的问题是:在这种情况下,这种方法是否非常不切实

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

您不允许编写任何生产代码,除非它是为了通过失败的单元测试

不允许您编写任何足以导致失败的单元测试;编译失败就是失败

不允许您编写的生产代码超过足以通过一个失败的单元测试的数量

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

开始编写一个测试用例=>run,并查看它是否失败

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

重构代码

然后重复这个循环

我的问题是:在这种情况下,这种方法是否非常不切实际和分散注意力


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

根据我对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的好处。

根据我对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的好处。

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

所以,当你后退一步,看到使用了3种算法时——这已经告诉我们,每种算法都应该有自己的方法。我们最初的方法只是调用其他方法来完成一些计算

还有一些其他的方向——没有法律阻止你根据自己的需要调整TDD。这导致了一种我称之为自底向上TDD的实践

它是这样的:我不是首先为我的一个巨大的方法编写一个测试,而是考虑在这个巨大的方法中我需要的不同部分。所以我只写下第一部分的测试;然后实现它。我对所有部分都这么做。随着时间的推移,这些部分变得越来越重要 增强,也许我会在途中将较小的部分合并为较大的部分,这很可能意味着将多个测试合并为一个较大的测试

这种技术可能意味着您可能会为您的大型方法生成一个单一的测试用例,但实际上您在构建大型解决方案时使用了TDD来测试小部分

换言之:与其为该方法的公共契约编写一个大型功能测试,不如先为我知道需要的小型助手方法编写测试。最终,这些助手将成为私人方法——因为直接测试它们是没有意义的。但是您可以保留早期测试中在大型公共方法上下文中有意义的部分


长话短说:所有这些技巧都是为了指导你找到自己的路。鉴于您有足够的经验进行动态设计,以这种方式使用TDD是可能的,而且实际上非常有趣

我认为另一个答案很好,但除此之外:如果一种方法需要您实现多个算法,您确定这种方法尊重单一责任原则吗?这听起来像是一种做太多事情的方法

所以,当你后退一步,看到使用了3种算法时——这已经告诉我们,每种算法都应该有自己的方法。我们最初的方法只是调用其他方法来完成一些计算

还有一些其他的方向——没有法律阻止你根据自己的需要调整TDD。这导致了一种我称之为自底向上TDD的实践

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

这种技术可能意味着您可能会为您的大型方法生成一个单一的测试用例,但实际上您在构建大型解决方案时使用了TDD来测试小部分

换言之:与其为该方法的公共契约编写一个大型功能测试,不如先为我知道需要的小型助手方法编写测试。最终,这些助手将成为私人方法——因为直接测试它们是没有意义的。但是您可以保留早期测试中在大型公共方法上下文中有意义的部分


长话短说:所有这些技巧都是为了指导你找到自己的路。鉴于您有足够的经验进行动态设计,以这种方式使用TDD是可能的,而且实际上非常有趣

参考Bob叔叔的主要因素Kata。参考Bob叔叔的主要因素Kata。