【问题标题】:DUnit Cannot create form. No MDI forms are currently activeDUnit 无法创建表单。当前没有 MDI 表单处于活动状态
【发布时间】:2014-03-16 17:57:41
【问题描述】:

嘿,我在 Delphi XE3 中的单元测试有问题我有一个项目,其中包含 1 个 MDIForm 和分配的 MDIChild 表单,那么问题是当我在 MDIChild 表单上运行测试时出现此错误:

TestAllDataSrouces: EInvalidOperation
at  $0064346F
SetUp FAILED: Cannot create form. No MDI forms are currently active

我的设置方法如下所示:

procedure TestTCustomerCard.SetUp;
begin
  FCustomerCard :=  TCustomerCard.Create(Application);
end;

我能做些什么来解决这个错误?到目前为止我试过了:

FCustomerCard :=  TCustomerCard.Create(Application.MainForm);

FCustomerCard :=  TCustomerCard.Create(nil);

procedure TestTCustomerCard.SetUp;
var
  a : TForm;
begin
  a := TForm.Create(nil);
  a.FormStyle := fsMDIForm;
  FCustomerCard :=  TCustomerCard.Create(a);
end;

我的测试是:

procedure TestTCustomerCard.TestAllDataSrouces;
var
  I: Integer;
begin
  for I := 0 to FCustomerCard.ComponentCount-1 do
  begin
    if (FCustomerCard.Components[i] is TcxLookupComboBox) then
    begin
      Check(TcxLookupComboBox(FCustomerCard.Components[i]).Properties.ListSource = nil,'Error no ListSource, Lookup: '+TcxLookupComboBox(FCustomerCard.Components[i]).Name+' Parent: '+TcxLookupComboBox(FCustomerCard.Components[i]).Parent.Name);
    end;
    if (FCustomerCard.Components[i] is TcxDBTextEdit) then
    begin
      Check(TcxDBTextEdit(FCustomerCard.Components[i]).DataBinding.DataSource = nil,'Error No DataSet, Text Edit: '+TcxDBTextEdit(FCustomerCard.Components[i]).Name+' Parent: '+TcxDBTextEdit(FCustomerCard.Components[i]).Parent.Name);
    end;
    if (FCustomerCard.Components[i] is TcxGridDBTableView) then
    begin
      Check(TcxGridDBTableView(FCustomerCard.Components[i]).DataController.DataSource = nil,'Error no Data Source, DB Grid View: '+TcxGridDBTableView(FCustomerCard.Components[i]).Name);
    end;
  end;
end;

演示项目:Here

【问题讨论】:

  • 错误信息解释了问题。没有 MDI 父窗体。
  • 有没有办法在测试中将表单创建为 MDIForm 而不是 Child?
  • 我不明白这个问题。
  • 这个问题有简单的解决方案吗?
  • DUnit 和 MDI 真的不能很好地混合。您是在测试 GUI,还是只是将所有业务逻辑与 GUI 混合在一起?

标签: delphi unit-testing mdi delphi-xe3 dunit


【解决方案1】:

您所做的更像是功能测试或集成测试。您正在检查您的 UI 是否设置正确。这种测试不同于单元测试。

单元测试应该检查如果你给一个模块某些输入,那么它们会产生某些输出。单元测试是本地化的。它们旨在独立于其他单元测试一个单元的行为。 UI 特别依赖于其他单元。它们从输入设备获取数据并在数据库上进行操作,并且总体上具有相当复杂的依赖关系。这使得它们成为单元测试的糟糕目标。

看看这个问题 - Unit tests vs Functional tests

要进行您想要的测试,最好自己制作一个可以正确设置环境并执行测试的工具。

【讨论】:

  • hmm 实际上是有道理的 :) 感谢我是这些测试的新手,但随着我在项目中的进展越来越多,我无法再逃避它们,因为一个错误的举动和你的一半逻辑已经消失或在我的我的数据源的一半:D
  • 我不明白为什么不能对 UI 进行单元测试。是的,它有依赖关系,但几乎所有代码也是如此。例如,使用模拟来处理它。也许 AirWolf 不想做单元测试。但我不同意您关于 UI 测试不是单元测试的看似建议。阅读他对我的回答的第一条评论。这对我来说听起来像是单元测试。
  • @DavidHeffernan 称它们为“单元测试”是令人困惑的,因为它们与单元测试的所有常见定义非常不同。当然,您可以使用单元测试框架进行功能和集成测试,但我认为测试本身不符合单元测试的条件。
  • @jp 如果它们是单元测试,我会称它们为单元测试。这里的单位是 GUI 层。我不是在谈论集成测试。为什么你认为 GUI 代码不能是一个单元?
  • 好吧,如果您测试 GUI,那么您是对的。但实际上,在大多数情况下,UI 测试不仅仅测试 UI,因为事件处理程序等中的所有行为。你真的编写 UI 测试测试没有任何行为的 UI,并且只需检查接线(我很好奇)?
【解决方案2】:

错误消息指出了问题。如果您需要一个 MDI 子窗体,它必须有一个 MDI 父窗体。并且该父表单必须是您程序的主要表单。在 DUnit 项目中很难实现。您的解决方案似乎是:

  • 将程序的主窗体设为 MDI 主窗体。我认为这将很难实现。
  • 使您的被测表单不是 MDI 子表单。
  • 找到一种不需要实例化此表单的测试方法。

【讨论】:

  • 是的,你是对的,我的数据库代码在我的程序中处于较低级别,我不测试该代码我要测试的是 GUI 组件属性以检查 DBEdit 组件是否具有分配给的数据源它,如果没有,那么测试失败,所以我基本上测试了它自己的组件,如果你能提供一个解决方案,如何让我的 MDIChild 表单不在我的 DUnit 项目中,我会接受你的答案。
  • 要么使主窗体成为 MDI 父窗体,要么使子窗体不是 MDI 子窗体。
  • 嗯,这不是那么简单,如果你认为你可以管理它,我整天都在尝试,如果你认为你可以管理它下载它具有相同代码的演示项目:)
  • 我为什么要下载代码然后尝试做你的工作?你问了一个高层次的问题。也许其他人想为您编写代码。
  • 这不是我的工作,这个项目是私人的,我想在 Delphi 中学习单元测试,我请专业人士帮助我,如果你不想帮助我也没关系。
【解决方案3】:

我遇到了同样的问题,我决定执行 David Heffernan 的建议和“让你的被测表单不是 MDI 子表单”。

在这里,我将描述如何实现这一目标。我已经在我的测试用例单元中进行了所有更改。

  1. 制作继承原始MDI子表单的测试表单

    type TTestCustomerCard = class(TCustomerCard) end;

    在你的测试用例类之前添加这个。

  2. 将 dfm 文件或表单(例如 CustomerCard.dfm)复制到 TestCustomerCard.dfm

  3. 在任何文本编辑器中打开 TestCustomerCard.dfm,删除行

    FormStyle = fsMDIChild因为 fsNormal 是默认值),

    更改第一行 object CustomerCard: TCustomerCard

    object TestCustomerCard: TTestCustomerCard

  4. 添加指令 {$R TestCustomerCard.dfm }

  5. 在您的 SetUp 方法中,而不是

    FCustomerCard := TCustomerCard.Create(Application);

    FCustomerCard := TTestCustomerCard.CreateNew(Application); InitComponentRes( 'TTESTCUSTOMERCARD', FCustomerCard );

【讨论】:

  • 我自己发现,这个决定有一个严重的缺点,因为没有调用表单 OnCreate 事件。所以我决定为我的表单制作特殊的构造函数`constructor TCustomerCard.CreateWithAnotherDFM;
  • constructor TCustomerCard.CreateWithAnotherDFM(AOwner: TComponent); begin CreateNew( AOwner ); InitComponentRes( 'TTESTCUSTOMERCARD', Self ); DoCreate; end;
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-04
  • 2018-02-16
  • 1970-01-01
  • 1970-01-01
  • 2018-04-17
相关资源
最近更新 更多