TDD:从哪里开始第一次测试

时间:2009-06-01 15:37:50

标签: tdd agile

所以我已经完成了一些单元测试,并且有编写测试的经验,但我还没有完全接受TDD作为设计工具。

我目前的项目是重新处理现有系统,该系统会在公司组装过程中生成序列号。由于查看现有系统,我了解当前的流程和工作流程。我还有一个新要求列表以及它们将如何修改工作流程。

我觉得我已经准备好开始编写程序了,我决定强迫自己最终从头到尾做TDD。

但现在我不知道从哪里开始。 (我也想知道我是否因为已经知道用户的程序流程而欺骗TDD流程。)

用户流程实际上是串行的,只是一系列步骤。例如,第一步是:

  • 用户提交制造订单编号并接收该订单物料清单的可序列化部件号列表

当用户选择其中一个部件号时,下一步就开始了。

所以我想我可以用这第一步作为起点。我知道我需要一段代码来获取制造订单号并返回零件号列表。

// This isn't what I'd want my code to end up looking like
// but it is the simplest statement of what I want
IList<string> partNumbers = GetPartNumbersForMfgOrder(string mfgOrder);

阅读Kent Becks的示例书,他谈到了选择小型测试。这看起来像一个非常大的黑盒子。它将需要一个mfg订单存储库,我必须抓取一个产品结构树来查找该mfg订单的所有适用的部件号,我甚至根本没有在代码中定义我的域模型。

所以一方面看起来像一个糟糕的开始 - 一个非常普通的高级功能。另一方面,我觉得如果我从较低的水平开始,我真的只是猜测我可能需要什么,这似乎是反TDD。


作为旁注......这就是你如何使用故事?

作为汇编程序 我想在制造订单上获得零件编号列表 这样我就可以选择哪一个序列化

说实话,汇编人永远不会这么说。所有汇编程序想要的是完成对mfg命令的操作:

作为汇编程序 我想用序列号标记零件 这样我就可以完成对mfg命令的操作

4 个答案:

答案 0 :(得分:16)

以下是我的开始。让我们假设您完全没有此应用程序的代码。

  1. 定义用户故事及其带来的业务价值:“作为用户,我想提交制造订单号和该订单的部件号列表,以便我可以将列表发送到库存系统”
  2. 从UI开始。使用三个字段创建一个非常简单的页面(假设它是一个Web应用程序):标签,列表和按钮。那太好了,不是吗?用户可以复制列表并发送到inv系统。
  3. 使用模式来设计你的设计,就像MVC一样。
  4. 为从UI调用的控制器方法定义测试。您在此处测试控制器是否正常工作,而不是数据是否正确:Assert.AreSame(3, controller.RetrieveParts(mfgOrder).Count)
  5. 编写一个控制器的简单实现,以确保返回一些内容:return new List<MfgOrder>{new MfgOrder(), new MfgOrder(), new MfgOrder()};例如,您还需要为MfgOrder实现类。
  6. 现在您的用户界面正在运行!工作不正常,但工作。因此,我们希望控制器从服务或DAO获取数据。在测试用例中创建一个Mock DAO对象,并添加一个期望方法“partsDao.GetPartsInMfgOrder()”被调用。
  7. 使用该方法创建DAO类。从控制器调用方法。您的控制器现已完成。
  8. 创建一个单独的测试来测试DAO,最后确保它从数据库中返回正确的数据。
  9. 继续迭代,直到完成所有操作。过了一会儿,你会习惯的。
  10. 这里的要点是将应用程序分成很小的部分,然后逐个测试这些小部件。

答案 1 :(得分:3)

作为开始测试,这是完全可以的。通过这个,您可以定义预期的行为 - 它应该如何工作。现在,如果你觉得你已经比你想要的更大一点了......你可以暂时忽略这个测试,然后编写一个更细粒度的测试,在中途部分或至少部分。然后进行其他测试,将您带入实现第一次大测试的目标。每一步都有Red-Green-Refactor。

小测试,我认为你不应该在一次测试中测试很多东西。例如在我用D上的这些参数调用Method1(),Method2()和Method3()之后,组件D.A,B和C处于state1,state2和state3中。 每个测试都应测试一件事。您可以搜索SO以获得良好测试的质量。但是我认为你的测试是一个小测试,因为它简短且专注于一项任务 - “从制造订单中获取PartNumbers”

更新:作为To-Try建议(来自Beck的书中的AFAIR),您可能想坐下来在一张纸上找到SUT的单行测试列表。现在您可以选择最简单的(您确信自己能够完成的测试。)以建立一些信心。或者你可以尝试一个你有80%信心,但有一些灰色区域(我的选择也是如此),因为它可以帮助你在路上学习一些关于SUT的东西。保留你不知道如何继续进行的那些......希望在更容易完成的时候它会更清晰。当它们变绿时,将它们一个接一个地打开。

答案 2 :(得分:1)

我认为你有一个良好的开端,但不是那么看。应该产生更多测试的测试对我来说是完全合理的,就好像你想一想,你知道什么是制造订单号或零件号吗?你必须建立可能导致其他测试的那些,但最终你会得到我认为的那些小测试。

这是一个可能需要一些分解的故事:

  • 作为用户,我想提交一份制造订单号,并接收该订单物料清单的可序列化部件号列表

我认为关键是要把事情一遍又一遍地分解成小块,这样才能构建整个事物。那种“分而治之”的技术有时很方便。 ;)

答案 3 :(得分:1)

好吧,当我第一次尝试使用TDD时,你碰到了与我完全相同的墙:)

从那以后,我放弃了它,仅仅因为它使重构过于昂贵 - 而且我倾向于在开发的初始阶段进行大量的重构。

有了那些脾气暴躁的话,我发现TDD最受监督和最重要的一个方面就是强迫你在实际实现它们之前定义你的类接口。当您需要将所有零件组装成一个大型产品(以及子产品;)时,这是一件非常好的事情。在编写第一个测试之前,您需要做的是在编码之前准备好您的域模型,部署模型以及最好的类图表 - 仅仅因为您需要识别不变量,最小值和最大值等之前,你可以测试它们。您应该能够在设计的单元测试级别上识别这些。

Soo,根据我的经验(不是一些喜欢将现实世界类比映射到OO:P的作者的经验),TDD应该是这样的:

  1. 根据需求规范创建您的部署图(ofc,没有什么是一成不变的) -
  2. 选择要实施的用户素材
  3. 创建或修改您的域模型以包含此故事
  4. 创建或修改您的类图以包含此故事(包括各种设计类)
  5. 识别测试向量。
  6. 根据您在步骤4中创建的界面创建测试
  7. 测试测试(!)。这是非常重要的一步..
  8. 实施课程
  9. 测试课程
  10. 与同事一起喝啤酒:)