2011-06-16 111 views
16

我在TD上读了很多关于TDD和单元测试的问题和答案,但是我没有发现任何答案:我从哪里开始?从头开始TDD项目

我和团队已经完成了一些项目,其中我们采用了单元测试,用于我们的代码......但先编码然后再进行单元测试。在开发过程的某个阶段,首先编写测试代码然后编写代码变得很自然,这使我们获得了更多的TDD风格。

现在我们想进行下一步,并尝试从一开始就用TDD启动一个新项目。这是问题......从哪里开始?当我没有代码时,我会写的第一个测试是什么?

比方说,为了让上下文思考,我必须开发一个以文档为中心的互联网应用程序,只需要一点工作流程和其他的东西。但是让我们从头开始:首先,我想创建一个简单的页面,列出存储在数据库表格中的所有文档(元数据)(很简单,呃?)。 这是我写的第一个测试?假设我正在使用Hibernate来访问数据库...我会测试ipothetical方法getAllDocuments()吗?但是,我应该使用一个模拟对象来替代Hibernate?那么我测试了什么?

我在这里有点困惑......另外,getAlDocuments()可能永远不会是一个生产方法......所有文档集合都会被排序并被某些东西过滤......它有意义吗? 任何建议将理解

编辑:

读你的答案(和http://programmers.stackexchange.com相似主题),我带着TDD的美好憧憬之后,但我仍然有一个dubt。

我总是尽管TDD是关于首先进行单元测试的...从未考虑过端到端测试。 但让我问:TDD说你必须写一个测试并看到一个编译错误;然后你创建类和方法,你会得到一个测试失败;然后你执行该方法并通过测试。在测试失败之前,您无法编写代码;在所有测试通过之前,你不能写另一个测试。我在这里吗?

作为第一次测试,我该如何进行端到端测试?我应该在所有层中编写所有代码,以通过测试。但后来我会有一大堆的类和方法都通过了我的端到端测试(尽管我不应该称之为集成测试?)。这意味着我不再需要单元测试,因为我已经有了一个覆盖我的代码的测试。我不能写一个已经通过的测试,这是违背TDD的实践。

帮助我理解这个提前进一步的步骤请

+0

我通常从写入自动加载器组件的测试开始。然后为我添加的每个班级。 – hakre 2011-06-16 16:37:03

+0

http://programmers.stackexchange.com/questions/84252/tdd-what-happens-before-the-first-unit-test/84255#84255 就在昨天问了同样的问题。 – 2011-06-16 16:55:55

回答

13

TDD是不是单元测试 - TDD是关于驾驶您的开发和架构的测试 - 与任何类型的,你需要的自动化测试。你看到了这个观点吗?

您正在开始一个新项目,并且您可能拥有一组功能。您应该有一些您要实现的功能的验收标准。这些标准可以定义您的顶级测试。让我们从一个端到端的测试开始(这可能很难,有时候因为它涉及到UI尚不存在),或者对这些验收标准进行集成测试。一旦你的测试失败了,你将继续实现与大型测试相关的功能,但是每个这个功能都将通过集成或单元测试再次被驱动。如果所有顶级测试都通过,则该功能已完成。

如果跳过大测试(终端到终端,集成),您将开发一套行之有效的单位,将在整合在一起,或者你的架构不会因为由单元定义的局部范围很不错要么不工作试验。集成和端到端测试为您提供全球范围。

这在书Growing Object-Oriented Software Guided by Tests中用大的例子(Java)进行了描述。

+0

请查看我更新的问题,并帮助我更深入地了解这些概念 – themarcuz 2011-06-30 21:09:47

+0

我检查了您的更新。我确认我描述的版本是不同的,并且不遵循这种方法(但Kent Beck描述的前TDD也不包括这些规则)。我的描述基于我引用的书,我发现它更有用。无论如何,你仍然可以调整你的流程,并遵循只有单一不完整单元测试的规则,但在单元测试时允许不完整的集成测试。 – 2011-07-04 10:30:18

1

从简单开始,您将在后面逐步添加功能。从快速设计开始:哪些类,哪些职责,哪些关系。您可以使用CRC cards。不要花太多时间在设计上,因为稍后您可以通过重构来改进它。选择最简单的类来开始实现系统的简单功能。例如,您可以先创建一个空白页面。

从一个类开始?它的对象应该做什么?你如何验证这是否正确完成?这是第一次测试。

您也可以在没有数据库的情况下启动,并将文档存储在平面文件中。稍后您将重构数据库。然后,您可以从getAllDocuments()函数开始。

2

我通常从上到下开始。在你的情况下,我将开始编写新页面的控制器逻辑。通过控制器,我的意思是在UI下面的代码层,嘲笑下面的所有内容。然后编写服务层(如果有的话),嘲笑数据层。最后还要测试数据层,并使用基础类的模拟(可以是您的情况中的ISession)。最后,我会编写每个数据层方法的单个集成测试并构建页面(html)。

2

由于您试图根据测试来推动开发,因此开始的方式是从您的第一个功能开始。例如,让我们假设您有一个上传文档的功能。你的第一个测试可能是:

public class DocumentManagementTest { 
    @Test public void allowsDocumentUploads() { 
    DocumentManagement dm = new DocumentManagement(); 
    Reader mockReader = new MockDocumentReader(); 

    Document result = dm.createDocument("Document name", mockReader); 

    assertEquals("Document name", result.getName()); 
    assertEquals(0, result.getTags().size()); 
    assertTrue(mockReader.fileWasRead); 
    } 
} 

我肯定会模拟出数据库入手,数据库的安装和拆卸是昂贵而脆。记住尽管做出非常小的步骤,但我上面展示的测试可能会在几次迭代中演变。以吸引更多的设计出后续测试可能是:

@Test public void allowsDocumentRenames() { ... } 
@Test public void allowsAddingTagsToExistingDocuments() { ... } 
@Test public void showsErrorWhenAddingDocumentThatAlreadyExists() { ... } 

一旦你已经建立了一个功能,如createDocument就可以围绕它的控制器。

public void doPost(HttpServletRequest req, HttpServletResponse resp) { 
    String name = req.getParameter("doc_name"); 
    Document d = docMgmt.createDocument(name, req.getInputStream()); 
    // Hand the newly created document to the view engine. 
} 

我不会太担心的控制器编写测试,因为它从一个复杂的角度来看相当低风险(如果控制器得到太多的代码,那么它可能是控制器代码属于一个气味另一个类,可能是您的DocumentManagement类)。

通过一次性构建功能并遵循SOLID原则,您将逐渐发展出一套具有出色测试覆盖率和相当不错的面向对象属性的系统。

干杯!

布兰登