在这篇关于delphiXtreme的有趣博客文章中,我读到了关于DUnit的内置GUI测试功能(基本上是在单元TGUITestCase
中定义的具有多个实用功能的替代测试用例类GUITesting
在GUI中调用动作)。我很满意它,直到我发现它不适用于模态形式。例如,如果第一个按钮显示模态配置表单,则以下序列将不起作用:
Click ('OpenConfigButton');
Click ('OkButton');
第二个Click
仅在模态窗体关闭时执行,我必须手动完成。
我不太了解模态表单在后台如何工作,但必须有一些方法来规避这种行为。天真地,我想以某种方式在一个线程中执行ShowModal
“,以便”主线程“保持响应。现在我知道在一个线程中运行ShowModal
可能会搞砸一切。还有其他选择吗?任何方法来规避ShowModal
的阻塞性质?有没有人在Delphi中进行GUI测试的经验?
我了解外部工具(来自QA或其他人)并且我们使用这些工具,但这个问题是关于IDE中的GUI测试。
谢谢!
答案 0 :(得分:22)
您无法通过调用ShowModal
来测试模态表单;因为正确地发现了这会导致您的测试用例代码“暂停”,而模态形式则等待用户交互。
原因是ShowModal
会将您切换为“辅助消息循环”,在表单关闭之前不会退出。
但是,仍然可以测试模态形式。
Show
方法显示通常的模态格式。Show
这一事实意味着不会通过设置模态结果来关闭表单。ModalResult
是否正确。您可以使用此技术通过非模态显式显示来测试特定的模态形式。但是,任何显示模式形式的测试代码(例如错误对话框)都会暂停您的测试用例。
即使您的示例代码: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;