【问题标题】:Help with TDD approach to a real world problem: linker帮助解决现实世界问题的 TDD 方法:链接器
【发布时间】:2010-12-17 16:52:24
【问题描述】:

我正在尝试学习 TDD。我已经看到了一些例子和讨论,这些例子和讨论如何轻松地从最小的功能开始对咖啡自动售货机固件进行 TDD。这些例子要么是原始的,要么是经过深思熟虑的,很难马上说出来。但这是一个现实世界的问题。

链接器。

最简单的链接器读取一个目标文件,执行魔术,然后写入一个可执行文件。我不认为我可以进一步简化它。我确实相信链接器设计可能会进化,但我完全不知道从哪里开始。关于如何解决这个问题的任何想法?


嗯,可能整个链接器对于第一个单元测试来说是个太大的问题。我可以预先设想一些粗略的结构。链接器的作用是:

  1. 将对象文件表示为段的集合。段包含代码、数据、符号定义和引用、调试信息等。
  2. 构建参考图并决定要保留哪些段。
  3. 根据某些规则将剩余的段打包到一个连续的地址空间中。
  4. 重新定位引用。

我的主要问题是项目符号 1。2、3 和 4 基本上采用常规数据结构并基于某些配置将其转换为依赖于平台的混乱。我可以设计那个,而且这个设计看起来是可行的。但是 1,它应该选择一个依赖于平台的混乱,采用几种支持的格式之一,并将其​​转换为常规结构。

这个任务看起来很笼统。它发生在您需要支持多种输入格式的任何地方,无论是图像处理、文档处理,等等。是否有可能进行TDD?似乎要么测试太简单,我很容易把它改成绿色,或者它有点复杂,我需要实现整个对象/图像/文档格式阅读器,这是很多代码。而且没有中间立场。

【问题讨论】:

  • 你以前写过链接器吗?如果您正在尝试学习 TDD,您可以考虑在相对熟悉的领域中的中小型项目中使用它。
  • 是的,我之前写过一个链接器。但是假设我想写一个图像查看器。大家应该比较熟悉了。假设我想手动解析所有格式,而不是使用任何库。我想设计它是可测试的等。我如何使用 TDD 来处理它?我从哪里开始?您的第一个测试是哪一个?

标签: unit-testing tdd


【解决方案1】:

这一切都非常有可能。
我脑海中的一个样本是NHAML

这是将纯文本转换为 .NET 本机代码的 ASP.NET ViewEngine。

您可以查看源代码并了解它是如何测试的。

【讨论】:

  • NHAML 的早期版本没有任何单元测试。他们在一定程度上涵盖了现有系统的设计。相反,我想从头开始尝试 TDD。我的问题是我无法分离一个足够小的任务来做一些有用的事情,并且在恕我直言,查看成熟的项目对此无济于事。
【解决方案2】:

我想我所做的就是想出层和块并细分到我可能正在考虑代码的地步,然后开始编写测试。

我认为您的测试应该非常简单:TDD 的力量不是单个测试,而是测试的总和。

我遵循的一个原则是方法应该适合屏幕 - 在这种情况下,测试通常足够简单。

您的设计应该允许您模拟较低的层,以便您只测试一层。

【讨论】:

  • 我有点明白这一切。但是话又说回来,您的方法看起来不像是测试驱动的设计。这更像是您在设计时考虑到了测试,这是完全不同的东西。
【解决方案3】:

TDD 是关于规范,而不是测试。

从您最简单的链接器规范来看,您的 TDD 测试只需检查在链接器魔术期间是否已创建可执行文件(如果您向其提供目标文件)。

然后你写一个链接器让你的测试成功,例如:

  • 检查输入文件是否为目标文件
  • 如果是,则生成“Hello World!”可执行文件(请注意,您的规范没有指定不同的目标文件会产生不同的可执行文件)

然后你完善你的规范和你的 TDD(这是你的四个要点)。

只要你能写出规范,你就可以写出TDD测试用例。

【讨论】:

  • 总有一天我会必须真正实现目标文件解析以通过我的测试。而且解析一个目标文件格式需要做很多工作,所以我的项目会红一阵子。我认为这将发生在任何高级块中。你如何避免这种情况?
  • 我倾向于将 TDD 与敏捷方法联系起来,在这种方法中,大项目被分成小的增量步骤。在每个步骤结束时,都会指定一个新功能(包括 TDD 测试用例)、开发并使用 TDD 测试用例进行测试。
【解决方案4】:

首先,看看 Freeman & Pryce 的“Growing Object Oriented Software Guided By Tests”。

现在,我尝试用几行来回答一个难题。

TDD 确实需要您思考(即设计)您将要做什么。你必须:

  1. 小步思考。非常小的步骤。
  2. 编写一个简短的测试,以证明下一小部分行为有效。
  3. 运行测试以显示失败
  4. 做最简单的事情让测试通过
  5. 无情重构,去除重复,改进代码结构
  6. 再次运行测试以确保一切正常
  7. 回到 1。

如何构建链接器的初始想法(设计)将指导您的初始测试。测试将强制执行模块化设计(因为每个测试只测试一个行为,并且对您编写的其他代码的依赖性应该最小)。

随着您的进行,您可能会发现您的想法发生了变化。您已经编写的测试将使您能够自信地重构。

测试应该很简单。将单个测试“破解”为绿色很容易。但是在每次“破解”之后,您都会进行重构。如果您在重构期间发现需要新的类或算法,则编写测试以驱动其接口。通过保持模块松散耦合(依赖注入、抽象基类、接口、函数指针等)并使用假冒、存根和模拟将被测代码与其他代码隔离开,确保测试只测试单一行为系统。

最后使用“客户”测试来确保您已经交付了功能特性。

这是一个很难改变心态的过程,但很有趣,也很有意义。诚实。

【讨论】:

  • 感谢您的参考书。这是一个有趣的阅读。作者似乎完全解决了我的担忧。
【解决方案5】:

您说得对,对我来说,链接器似乎比“单元”大一点,TDD 并不能让您坐下来思考如何将问题分解为单元。 The Sudoku saga 很好地说明了如果您不先思考会出现什么问题!

专注于您的第 1 点,您已经通过列出可以在段中出现的事物种类,并暗示您需要支持多种格式,从而描述了一个很好的单元(功能)集合。为什么不从处理一个简单的案例开始,例如,一个只包含开发平台二进制格式的数据段的文件?您可以在测试中简单地将文件硬编码为二进制数组,然后检查它是否正确解释。然后选择另一个简单的案例,并对其进行测试。继续。

现在神奇的一点是,很快您就会在代码和测试中看到重复的结构,并且因为您已经进行了测试,所以您可以非常积极地重构它。我怀疑这是你还没有经历过的一点,因为你说“看起来要么测试太简单,我很容易把它改成绿色,要么有点复杂,我需要实现整个对象/图像/文档格式阅读器,其中包含大量代码。而且没有中间立场。" 关键是您应该将它们全部修改为绿色,但因为您是这样做你也在寻找你的黑客模式。

我以这种方式编写了一个(非常简单的)编译器,而且它大部分工作得很好。对于每个句法结构,我编写了我能想到的最小的程序,它以某种可观察的方式使用它,并让测试编译程序并检查它是否按预期工作。我使用了一个适当的解析器生成器,因为您无法合理地 TDD 进入其中一个(您需要使用一点先见之明!)大约三个周期后,很明显我正在重复代码以遍历语法树,所以被重构为类似于访问者的东西。

我也进行过更大规模的验收测试,但最终我认为这些测试并没有像单元测试那样捕捉到太多。

【讨论】:

  • 谢谢,我想我开始明白了。至少我已经有一些初始测试已经运行并通过了,他们花了非常多的时间来编写,他们测试了一些有用的东西,并且他们测试的代码做了一些有用的事情。实际上,我在测试中嵌入了几个字节长的微小目标文件,并确保按我的预期解析它们。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-07
  • 2023-04-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多