在我的记忆中,大多数人告诉我,我应该从上到下进行设计。如果我想实现一个网页,我应该在纸上绘制或绘制这个页面,然后将其分成一些功能。对于每个功能,我尝试设计外部API,并分别实现它们的内部。
但在TDD中,他们说我应该考虑一个非常小的功能(一种方法?),首先编写测试,失败,实现它并通过测试。编写它们是最后一步。我无法想象它如何获得良好的API。
最奇怪的是,他们说TDD不仅是单元测试,还有功能测试。我认为这意味着自上而下。如果存在由方法B,C和D组成的功能A.由于TDD,我首先为A编写功能测试。但是...... B,C,D都是未实现的。我应该使用三个存根吗?如果B依赖于其他三种方法?
我用TDD写了一些小程序。但是当我使用GUI攻击应用程序时,我陷入了困境。
答案 0 :(得分:2)
由于TDD从你可以从外面看到的东西(你目前正在处理的任何项目)开始,我不确定它是否有资格作为自下而上。
将TDD推向极端(例如,在XP中,也就是极端编程),您肯定会从最终用户的角度出发,并且只编写尽可能多的代码来传递到目前为止创建的测试。如果你发现自己开始使用某些内部函数的测试,然后才能达到更高级别的测试(加上你编写的代码的良好设计以使这些测试通过)需要这个例程,那么你正在研究其他一些范例,不严格的TDD - 因为没有测试告诉你首先编写该方法。并不是说这一定是件坏事,但是你遇到的任何问题都不是TDD方法论的真正原因之一。
对于GUI编程,当然,即使在创建代码之前,您也有自动化测试的标准问题。我只知道Web应用程序的好工具;如果你在桌面案例中对这个主题有很好的指导,我很乐意看到它们。
答案 1 :(得分:0)
我一直在为我的rails项目编写rspec测试,在代码/测试之间保持大约1:1.6的比例。我从来没有真正解决过先写什么或者依赖什么的问题。如果我想写的方法A由B和C组成,那么我将首先使用正确的测试来实现B和C.对我来说,只要测试是好的和精确的,序列就不那么重要了。
所以,我并没有像你描述的那样使用存根,但只有当功能已经存在并且我只是想绕过/绕过它时。
顺便说一下,它被认为是一种自上而下的方法。这是Rspec Book的摘录:如果您询问有经验的软件 交付人员为什么他们运行一个项目 像这样,前面加载所有 规划和分析,然后进入详细的设计和 编程,最后只是真正集成和测试它们 看着远处凝视远方 比他们年龄大,耐心 解释这是为了缓解 反对指数成本 改变 - 引入的原则 改变或发现缺陷 成倍地变得更加昂贵 你发现它的后来。该 自上而下的方法似乎是唯一的 明智的对冲方式 发现缺陷的可能性 当天晚些时候。
答案 2 :(得分:0)
我会说这是自上而下的。假设我有一个PDF,里面有4个不同的文档,我正在编写软件将它们分成4个单独的文档而不是单个文档,我可能会写的第一个测试是:
// Note: Keeping this very abstract @Test public void splitsDocumentsAccordingToDocumentType() { List docs = new DocumentProcessor().split(new SinglePdfWithFourDocs()); assertEquals(4, docs()); ... }
我认为DocumentProcessor.split()
方法与示例中的“A”类似。我现在可以在单个方法split
中实现整个算法并使测试通过。我甚至不需要“B”或“C”对吗?知道自己是一名优秀的开发人员并且在考虑1500线方法时感到畏缩,您将开始寻找将代码重构为更合适的设计的方法。也许您看到可以从这段代码中分离出两个额外的对象(职责):
1)解析文件的内容以查找单个文档和 2)从文件系统中读取和写入文档
让我们先解决#1问题。使用一些“Extract Method”重构来本地化与内容解析相关的代码,然后进行“Extract Class”重构,将这些方法拉出到名为DocumentParser
的类中。在你的例子中,这可能与“B”类似。如果您愿意,可以将与文档解析相关的测试从DocumentProcessorTest
移动到新的DocumentParserTest
,并模拟或存根DocumentParser
中的DocumentProcessorTest
。
对于#2来说,它几乎是泡沫,冲洗,重复,你最终会得到像DocumentSerializer
类,AKA“C”这样的东西。您也可以在DocumentProcessorTest
中对此进行模拟,现在您没有文件I / O,并且测试驱动了一个具有两个额外协作者的组件,而无需设计整个类(使用单独的方法)。请注意,我们采用了“外部”方法,这实际上可以实现重构。
由于