我在TD上阅读了很多关于TDD和单元测试的问题和答案,但我没有找到答案:我从哪里开始?
我和团队已经完成了一些项目,其中我们采用了单元测试,我们的代码......但首先是代码,然后是单元测试。在开发过程的某个阶段,首先编写测试然后编写代码变得非常自然,这使我们更具TDD风格。
现在我们想进行下一步,并尝试从一开始就使用TDD开始一个新项目。这是问题......从哪里开始?当我没有代码时,这是我写的第一个测试?
让我们说,只是要考虑一个背景,我必须开发一个互联网应用程序,以文档为中心,有一点工作流程......还有别的东西。 但是让我们从头开始:首先,我想创建一个简单的页面,列出存储在DB上的表中的所有文档(元数据)(非常简单,嗯?)。 这是我写的第一个测试?假设我正在使用Hibernate访问数据库...我会测试ipothetical方法getAllDocuments()吗?但是我应该使用模拟对象来替换Hibernate吗?那我在测试什么呢?
我在这里有点混淆......而且getAlDocuments()可能永远不会是一个生产方法...所有的文档集合都将按照某些东西进行排序和过滤......这有意义吗? 任何建议将不胜感激
编辑:
在阅读了你的答案(以及http://programmers.stackexchange.com处的类似帖子)之后,我对TDD有了更好的看法,但我仍然有一个dubt。
我总是认为TDD是关于首先进行单元测试的......从未考虑过端到端测试。 但是让我问一下:TDD说你必须编写测试并看到编译错误;然后你创建类和方法,你得到测试失败;然后你实现方法并通过测试。在测试失败之前,您无法编写代码;在所有测试通过之前,你不能写另一个测试。我在这儿吗?
如何进行端到端测试作为我的第一次测试?我应该在所有层中编写所有代码,以便让测试通过。但是接下来我将通过端到端测试测试一堆类和方法(不应该把它称为集成测试吗?)。这意味着我不再需要单元测试,因为我已经有一个覆盖我的代码的测试。我不能写一个已经通过的测试,这是针对TDD的实践。
请帮助我进一步了解这一步
答案 0 :(得分:14)
您正在开始一个新项目,并且您可能拥有一组功能。您应该对要实施的功能有一些验收标准。这些标准可以定义您的顶级测试。让我们从端到端测试开始(这有时候很难,因为它涉及到尚不存在的UI)或者这些验收标准的集成测试。一旦测试失败,您将继续实现与大型测试相关的功能,但每个此功能将通过集成或单元测试再次驱动。如果所有顶级测试都通过,则该功能已完成。
如果跳过大型测试(端到端,集成),您将开发一组经过良好测试的单元,这些单元在集成在一起时将无法工作,或者由于单元测试定义的局部范围,您的架构将不会很好。集成和端到端测试为您提供全球范围。
本书Growing Object-Oriented Software Guided by Tests中的大型示例(Java)对此进行了描述。
答案 1 :(得分:2)
我通常从上到下开始。在您的情况下,我将首先编写新页面的控制器逻辑。通过控制器,我的意思是在UI下面的代码层,模拟下面的一切。然后编写服务层(如果有的话),模拟数据层。最后使用底层类的模拟测试数据层(在您的情况下可能是ISession)。最后,我将编写每个数据层方法的单一集成测试并构建页面(html)。
答案 2 :(得分: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原则,您将慢慢发展一个具有出色测试覆盖率和非常好的OO属性的系统。
干杯!
布兰登
答案 3 :(得分:1)
从简单开始,稍后您将逐步添加功能。从快速设计开始:哪些类,哪些职责,哪些关系。您可以使用CRC cards。不要在设计上花太多时间,因为稍后您可以通过重构来改进它。选择最简单的类来开始实现系统的简单功能。例如,您可以先创建一个空页面。
从一堂课开始?它的对象应该做什么?如何验证这是否正确完成?这是第一次测试。
您也可以在没有数据库的情况下启动,并将文档存储在平面文件中。稍后您将重构数据库。然后你可以从getAllDocuments()函数开始。