delphi - 对返回复杂数据集的类进行单元测试

标签 delphi unit-testing refactoring legacy-code

经过几个月的挫败感和花费时间在以前的开发人员的巫毒娃娃中插入针之后,我决定最好尝试重构遗留代码。

我已经订购了Micheal Feather's book ,我喜欢Fowler's refactoring我用 DUnit 做了一些示例项目.

因此,即使我没有掌握该主题,我也觉得是时候采取行动并将一些想法付诸实践了。

我所处理的代码几乎 100% 的业务逻辑都被困在 UI 中,而且都是过程式编程(除了少数异常(exception))。该应用程序开始时又快又脏,并且继续如此。

现在为所有应用程序编写测试对我来说是一项毫无意义的任务,但我想尝试对我需要重构的东西进行单元测试。

大型“TForm 业务逻辑类”执行的复杂任务之一是读取数据库数据、进行一些计算并填充调度程序组件。我想删除读取数据库数据和计算部分并将此任务分配给一个新类。当然,这是改进当前设计的一种方法,它不是从头开始的最佳方法,但我想这样做,因为这个新类返回的数据在其他方面也很有用,例如现在我已被要求发送调度程序数据的电子邮件通知。

因此,为了避免大量复制和粘贴操作,我需要新类。

现在,调度程序由一个巨大的数据集(大小和字段数量巨大)填充,可能第一个重构步骤可能是从新类获取数据集。但将来我最好使用一个新类(例如 TSchedulerData 或其他与调度程序绑定(bind)较少的名称)来管理数据,并且我可以拥有一个 TSchedulerData 对象,而不是使用数据集作为结果。

由于重构是小步进行的,并且需要测试才能更好地重构,所以我对如何进行有点困惑。

我不清楚以下几点:

1)如何测试复杂的数据集?我是否应该运行工作应用程序,将一个结果集保存到 xml,然后编写一个使用包含该 xml 数据的 TClientDataSet 的测试?

2) 我需要在多大程度上关心 TSchedulerData?我的意思是,我不是 100% 确定我会使用 TSchedulerData,也许我会坚持使用数据集,无论如何,考虑创建将在 2 周内丢弃的复杂测试对于 DUnitNewbee 来说并不有吸引力。无论如何,这可能就是它的工作原理。我无法想象如果没有测试我会面临多少错误。

最后说明:我知道有人认为从头开始重写是更好的选择,但这不是一个选择。 “该应用程序规模庞大,今天就已出售,今天需要新功能才能不倒闭”。这就是我被告知的,无论如何重构可以挽救我的生命并延长应用程序的生命周期。

最佳答案

您的最终目标是将 UI、数据存储和业务逻辑分离到不同的层中。

使用自动测试框架测试 UI 非常困难。您最终会希望将尽可能多的业务逻辑与 UI 分离。这可以使用各种模型/ View /* 模式之一来完成。我更喜欢 MVP 被动 View ,它试图使 UI 只不过是一个界面。如果您使用数据集 MVP 监督 Controller 可能更适合。

数据存储需要有自己的测试套件,但这些测试与单元测试不同(尽管您可以使用相同的单元测试框架),而且通常数量较少。您可以摆脱这种情况,因为大部分繁重的工作是由第三方数据组件和 dbms(在您的情况下为 T*Dataset)完成的。这些是集成测试。基本上确保您的代码与供应商的代码兼容。如果您在数据库中定义了任何存储过程,则也需要。它们比单元测试慢得多,并且不需要经常运行。

业务逻辑是您最想测试的。每个计算、循环或分支都应该至少有一个测试(越多越好)。在遗留代码中,此逻辑通常直接接触 UI 和数据库,并在单个函数中执行多项操作。这里提取方法是你的 friend 。提取方法的好地方是:

for I:=0 to List.Count - 1 do
begin
  //HERE
end;

if /*HERE if its a complex condition*/ then
begin
  //HERE
end
else
begin
  //HERE
end

Answer := Var1 / Var2 + Var1 * Var3; //HERE

当您遇到这些撤离点之一时

  1. 确定您希望新方法的方法签名是什么样的:方法名称、参数、返回值。
  2. 编写一个测试来调用它并检查预期结果。
  3. 提取方法。

如果一切顺利,您将拥有一个新提取的方法,并且至少有一个通过单元测试。

Delphi 内置的提取方法没有为您提供任何调整签名的方法,因此如果这是您自己的选择,您必须在提取后解决它。您还需要公开新方法,以便您的测试可以访问它。有些人不愿公开私有(private)实用程序方法,但在这个早期阶段你别无选择。一旦您取得了足够的进展,您将开始看到您提取的一些实用方法属于它们自己的类(在这种情况下,无论如何它们都必须是公共(public)的),而其他方法可以设为私有(private)/ protected 并间接测试通过测试依赖于它们的方法。

随着您的测试套件的增长,您需要在每次更改后运行它们,以确保您的最新更改不会破坏其他地方的内容。

这个主题太大,无法在答案中完全涵盖。当这本书到达时,您会发现您的绝大多数问题都得到了解答。

关于delphi - 对返回复杂数据集的类进行单元测试,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3966294/

相关文章:

javascript - 如何取消使用 sajax 进行的 ajax 调用?

Delphi 7 - 使用 Canvas 绘制带有透明核心的圆

delphi - 避免在 TDataSet 中添加重复的列名

c++ - 使用指针将代码翻译成 Pascal 中的程序集 - Delphi

c# - 模拟具有不同签名的方法,其中一个方法具有 Object 作为参数类型

java - 测试中未抛出 NullPointerExceptions

ios - 向下转换/子类 UIViewController 用于单元测试中的模拟

scala - 如何使用 Scalaz 或 Cats 重构抛出异常的函数

android - Kitkat 应用程序将在 Lollipop 版本中运行吗

java - Intellij - 使用委托(delegate)类重构 getter 和 setter