Delphi GUI测试和模态表单

时间:2011-07-04 15:30:09

标签: delphi testing delphi-xe gui-testing

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

Click ('OpenConfigButton');
Click ('OkButton');

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

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

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

谢谢!

2 个答案:

答案 0 :(得分:22)

您无法通过调用ShowModal来测试模态表单;因为正确地发现了这会导致您的测试用例代码“暂停”,而模态形式则等待用户交互。

原因是ShowModal会将您切换为“辅助消息循环”,在表单关闭之前不会退出。

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

  1. 使用普通 Show方法显示通常的模态格式
  2. 这允许您的测试用例代码继续,并模拟用户操作。
  3. 可以正常测试这些动作和效果。
  4. 您需要对模态形式进行额外的测试:
    1. 通常通过设置模态结果来关闭模态窗体。
    2. 您使用Show这一事实意味着不会通过设置模态结果来关闭表单。
    3. 这很好,因为如果您现在模拟单击“确定”按钮...
    4. 您只需检查ModalResult是否正确。
  5. 警告

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

    即使您的示例代码:Click ('OpenConfigButton');也会导致调用ShowModal,并且无法以此方式进行测试。

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

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

    所以:

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

    IErrorMessage = interface
      procedure ShowError(AMsg: String);
    end;
    

    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;
    

    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;
    

    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;
    

    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;
    

答案 1 :(得分:11)

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

最近我一直在研究一个简单的库来处理这个问题。您可以从这里下载代码: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;