【问题标题】:Providing data to Mock for Unit Testing为 Mock 提供数据以进行单元测试
【发布时间】:2014-02-03 15:33:13
【问题描述】:

我正在执行单元测试的课程滚动数据库表的每条记录,并将字段中的值与前一个值相加。以下是精简到骨子里的类:

procedure TSumList.Sum;
var
  FSum:integer;
begin
  FSum:=0;
  FDB.First;
  while not FDB.EOF do
  begin
    FSum:=FSum+FDB.GetAmount;
    FDB.Next;
  end;
end;

FDB 指的是名为 IIDBTable 的 DB 模拟接口。

以下是依赖注入的数据库模拟:

  IIDBTable = interface
    ['{A299D1D6-93AF-45CC-8DE2-9A4EE188C352}']
    procedure First;
    procedure Next;
    function EOF : boolean;
    function GetAmount:integer;
  end;

  TMockDBTable = class (TInterfacedObject,IDBTable)
    procedure First;
    procedure Next;
    function EOF : boolean;
    function GetAmount:integer;
  end;

问题是我不知道如何为模拟提供数据以进行测试。当然,我可以添加一个额外的过程,比如 AddValues(aAmount:integer),它可以完成这项工作,但在这种情况下,我最终也会在生产中使用这个额外的过程,我不需要它。

这方面的最佳做法是什么?

我在 Delphi 框架中使用 Spring

【问题讨论】:

  • 缺少一点信息:TSumList 如何知道IIDBTable?另外:额外的方法可以在 TMockDBTable 上,而不是在接口上。或者你可以在 TMockDBTable 的构造函数中传递数据
  • 您需要在TMockDBTable 中实现模拟功能。由于TMockDBTable 提供了在测试时使用的IDBTable 的实现,因此您需要使TMockDBTable 能够接收模拟数据。我不明白您为什么认为需要在生产代码中添加任何内容。您没有在生产代码中添加TMockDBTable 是吗?

标签: delphi unit-testing mocking spring4d


【解决方案1】:

您还可以使用 DSharp 模拟(或 Delphi 模拟)。

这将是 DSharp 的设置代码(Delphi Mocks 应该类似)

var
  mockDBTable: Mock<IIDBTable>;
begin
  mockDBTable.Setup.WillExecute.Once.WhenCalling.First;
  mockDBTable.Setup.WillReturn(False).Exactly(5).WhenCalling.EOF;
  mockDBTable.Setup.WillReturn(True).Once.WhenCalling.EOF;
  mockDBTable.Setup.WillReturn(5).Once.WhenCalling.GetAmount;
  mockDBTable.Setup.WillReturn(4).Once.WhenCalling.GetAmount;
  mockDBTable.Setup.WillReturn(3).Once.WhenCalling.GetAmount;
  mockDBTable.Setup.WillReturn(2).Once.WhenCalling.GetAmount;
  mockDBTable.Setup.WillReturn(1).Once.WhenCalling.GetAmount;
  mockDBTable.Setup.WillExecute.Exactly(5).WhenCalling.Next;

您在此处所做的是指定您希望调用的内容和返回的内容。 这使您无需手动编写模拟类并为其提供数据。

【讨论】:

  • 也许只有我一个人,但我发现这样的代码更像是一个障碍而不是帮助。我不确定你从什么中拯救了自己,但我想我会选择每次手动制作一个模拟类!
  • 我同意在这种情况下设置代码有点冗长,这样做可能不是最好的方法,因为在这种情况下你的模拟更以数据为中心。因此,用测试数据提供一个“真实的”IIDBTable 实例可能是一种更好的方法。但我不会说每次都写模拟类是更好的方法。您显然没有过多地使用自动模拟。否则你可能会有不同的想法。在此示例中也不清楚,因为您正在使用接口的每个方法,但您可能会遇到有几种方法要实现(即使是空的)但只使用一种的情况。
  • 是的,你说得对,“每次”都太强了。我敢肯定,我真的不是故意的。我正在孤立地看这个例子。以数据为中心使自动模拟方法在这里变得不那么有吸引力,但我也为流畅的风格而苦恼。再次,也许是由于缺乏熟悉。我喜欢写 NUnit 测试,但是当我写出流利的风格时,我发现很难阅读我正在写的东西。无论如何,+1,这是一个很好的答案。
  • Delphi Mock 方法是我尝试的第一个方法,但我不知道它提供了如此扩展的语法。我也 +1。
  • 我的 Delphi Mock 版本没有更新还是没有“Once”可用?我刚刚从github.com/VSoftTechnologies/Delphi-Mocks点击“下载ZIP”按钮下载了它
【解决方案2】:

为模拟类添加一个额外的方法AddValues。它只会存在于那个模拟类中,而不存在于真实类中,所以你反对额外代码会出现在生产环境中是没有根据的。

我怀疑您认为代码需要投入生产,因为您只通过接口处理模拟对象。额外的方法将存在于接口中,因此需要在类的生产版本中实现,即使它不属于那里。

相反,实例化TMockDBTable 并通过对象引用访问它以设置它,但它是测试所需的。准备好后,然后切换到通过IIDBTable 接口使用它。

另一种选择是通过添加到其构造函数的参数将数据输入类。然后,您甚至不需要额外的方法,因此没有在接口或生产类上定义该方法的诱惑。没有理由你的模拟类的构造函数需要看起来像生产类的构造函数,因为这些类根本不相关。构造函数不是接口定义的一部分。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-01-10
    • 2012-04-11
    相关资源
    最近更新 更多