Delphi GUI测试和模态表单

jpf*_*ius 13 delphi testing gui-testing delphi-xe

在这篇关于delphiXtreme的有趣博客文章中,我读到了关于DUnit的内置GUI测试功能(基本上是TGUITestCase在单元GUITesting中定义的替代测试用例类,其具有用于在GUI中调用动作的若干实用程序功能).我很满意它,直到我发现它不适用于模态形式.例如,如果第一个按钮显示模态配置表单,则以下序列将不起作用:

Click ('OpenConfigButton');
Click ('OkButton');
Run Code Online (Sandbox Code Playgroud)

第二个Click只在模态窗体关闭时执行,我必须手动完成.

我不太了解模态表单在后台如何工作,但必须有一些方法来规避这种行为.天真地,我想以某种方式执行ShowModal"在一个线程中",以便"主线程"保持响应.现在我知道ShowModal在线程中运行可能会搞砸一切.还有其他选择吗?任何方式来规避一个阻止性质 ShowModal?有没有人在Delphi中进行GUI测试的经验?

我了解外部工具(来自QA或其他人),我们使用这些工具,但这个问题是关于IDE中的GUI测试.

谢谢!

Dis*_*ned 22

你不能通过调用来测试模态形式ShowModal; 因为你已经非常正确地发现,这导致你的测试用例代码'暂停',而模态形式等待用户交互.

这样做的原因是ShowModal切换到"辅助消息循环",在表单关闭之前不会退出.

但是,仍然可以测试模态形式.

  1. 使用常规方法显示通常的Modal形式. Show
  2. 这允许您的测试用例代码继续,并模拟用户操作.
  3. 这些动作和效果可以正常测试.
  4. 您将需要对Modal表单进行额外的测试:
    1. 通常通过设置模态结果来关闭模态形式.
    2. 您使用的事实Show意味着不会通过设置模态结果来关闭表单.
    3. 哪个没问题,因为如果您现在模拟单击"确定"按钮...
    4. 你可以简单地检查一下ModalResult是否正确.

警告

您可以使用此技术通过非模态地显示它来测试特定的模态形式.但是,任何显示模式形式的测试代码(例如错误对话框)都会暂停您的测试用例.

甚至你的示例代码:Click ('OpenConfigButton');导致ShowModal被调用,并且无法以这种方式进行测试.

要解决此问题,您需要将"show命令"注入您的应用程序.如果您不喜欢依赖注入,我建议在You Tube上提供Misko Hevery的Clean Code Talks视频.然后在测试时,注入一个合适版本的"show命令",它不会显示模态形式.

例如,如果单击"确定"按钮时验证失败,则模式窗体可能会显示错误对话框.

所以:

1)定义接口(或抽象基类)以显示错误消息.

IErrorMessage = interface
  procedure ShowError(AMsg: String);
end;
Run Code Online (Sandbox Code Playgroud)

2)您正在测试的表单可以包含对接口(FErrorMessage: IErrorMessage)的注入引用,并在验证失败时使用它来显示错误.

procedure TForm1.OnOkClick;
begin
  if (Edit1.Text = '') then
    FErrorMessage.ShowError('Please fill in your name');
  else
    ModalResult := mrOk; //which would close the form if shown modally
end;
Run Code Online (Sandbox Code Playgroud)

3)为生产代码使用/注入的IErrorMessage的默认版本将像往常一样显示消息.

4)测试代码将注入IErrorMessage的模拟版本,以防止您的测试被暂停.

5)您的测试现在可以执行通常会显示错误消息的案例.

procedure TTestClass.TestValidationOfBlankEdit;
begin
  Form1.Show; //non-modally
  //Do not set a value for Edit1.Text;
  Click('OkButton');
  CheckEquals(0, Form1.ModalResult);  //Note the form should NOT close if validation fails
end;
Run Code Online (Sandbox Code Playgroud)

6)您可以进一步模拟IErrorMessage来实际验证消息文本.

TMockErrorMessage = class(TInterfaceObject, IErrorMessage)
private
  FLastErrorMsg: String;
protected
  procedure ShowError(AMsg: String); //Implementaion trivial
public
  property LastErrorMsg: String read FLastErrorMsg;
end;

TTestClass = class(TGUITesting)
private
  //NOTE!
  //On the test class you keep a reference to the object type - NOT the interface type
  //This is so you can access the LastErrorMsg property
  FMockErrorMessage: TMockErrorMessage;
  ...
end;

procedure TTestClass.SetUp;
begin
  FMockErrorMessage := TMockErrorMessage.Create;
  //You need to ensure that reference counting doesn't result in the
  //object being destroyed before you're done using it from the 
  //object reference you're holding.
  //There are a few techniques: My preference is to explicitly _AddRef 
  //immediately after construction, and _Release when I would 
  //otherwise have destroyed the object.
end;
Run Code Online (Sandbox Code Playgroud)

7)现在早期的测试成为:

procedure TTestClass.TestValidationOfBlankEdit;
begin
  Form1.Show; //non-modally
  //Do not set a value for Edit1.Text;
  Click('OkButton');
  CheckEquals(0, Form1.ModalResult);  //Note the form should NOT close if validation fails
  CheckEqulsString('Please fill in your name', FMockErrorMessage.LastErrorMsg);
end;
Run Code Online (Sandbox Code Playgroud)


tom*_*azy 11

实际上有一种方法可以在Delphi中测试模态窗口.当显示模态窗口时,您的应用程序仍会处理Windows消息,因此您可以在显示模式窗口之前将消息发布到某个帮助窗口.然后您的消息将从模态循环处理,允许您在模态窗口仍然可见时执行代码.

最近我一直在研究一个简单的库来处理这个问题.您可以从此处下载代码:https://github.com/tomazy/DelphiUtils(请参阅:FutureWindows.pas).

样品用法:

uses
  Forms,
  FutureWindows;

procedure TFutureWindowsTestCase.TestSample;
begin
  TFutureWindows.Expect(TForm.ClassName)
    .ExecProc(
       procedure (const AWindow: IWindow)
       var
         myForm: TForm;
       begin
         myForm := AWindow.AsControl as TForm;

         CheckEquals('', myForm.Caption);

         myForm.Caption := 'test caption';
         myForm.Close();
       end
    );

  with TForm.Create(Application) do
  try
    Caption := '';

    ShowModal();

    CheckEquals('test caption', Caption);
  finally
    Free;
  end;
end;
Run Code Online (Sandbox Code Playgroud)