我正在阅读 Kent Beck 的“示例测试驱动开发”一书(此处提供 pdf 文件),其中他讨论了多币种货币问题并逐步设计了一个解决方案,通过引入测试并因此指定其公共方法类和反复重构以适应新的测试/行为。
他从上课Franc
开始。Dollar
然后考虑了这些类的接口并查看了代码并发现了重复,他将这两个类重构为都派生自一个Money
类。
几次测试之后,他意识到类Franc
并Dollar
没有真正的意义,并currency
为货币引入了一个字符串,最后当它需要对不同货币的货币求和时,他提出了一个Money
名为的类的超类,expression
它简化了对对 Money 的附加操作,例如对不同货币求和。
综上所述,在我的理解中,指导 TDD 设计过程的是对象的行为和测试期望。我的问题是,由于函数式编程较少关注对象并且更多地面向类型类/特征,这是否意味着这种测试驱动的设计实践并不真正适合函数式风格?是否可以认为属性驱动的测试在设计过程中与功能风格发挥类似的作用?
编辑:
在基于属性的测试中,人们会为方法引入属性,无论给定方法的实现如何,这些属性都应该保持不变。在正常测试中,被验证的最重要的属性是实现的正确性。正如 sisyphus指出的那样,存在断言论点的先验知识从何而来的问题,因此检查正确性并不一定能给我们提供全貌。
还有其他注意它们的属性,可能会很有启发性。例如,如果我要编写一个排序函数并拥有sortedList=mySort(myList)
,无论实现如何,我都希望sortedList
和myList
具有相同的长度。如果不是这样,我可能做错了什么,即使测试可能对所有其他情况都有效。
另一个例子,一个MaxElement(list)
返回Option[int]
整数列表作为输入的函数,如果list
不为空,则应该总是返回 Some[int]。
要检查这些属性,可以为这些方法的输入编写一个定制的生成器,然后要求测试框架生成足够的输入实例并测试属性以保证其有效性几乎可以确定。例如,在货币问题的情况下,可以编写一个生成器来生成各种类型的货币子类并检查它们的相等性,这将自动揭示出考虑贝克明确编写的一些相等性测试的必要性.
重新表述这个问题,我很想知道他是否犯了一个自然错误的原因,即明确的类Franc
和Dollar
- 他后来意识到,当他注意到两个类中某些方法的重复实现时 - 与面向对象方法密切相关在建模系统中。是否可以说函数式编程不需要这种迭代设计过程,仅仅是因为它不是从对象开始,而是进行函数分解。或者从另一个角度来看问题,测试是否会导致功能风格上的系统设计改变?