您不能通过调用来测试模态表单ShowModal
;因为正如您正确地发现的那样,这会导致您的测试用例代码在模态表单等待用户交互时“暂停”。
这样做的原因是ShowModal
将您切换到一个“辅助消息循环”,该循环在表单关闭之前不会退出。
但是,仍然可以测试模态形式。
- 使用normal方法显示通常的 Modal 形式。
Show
- 这允许您的测试用例代码继续,并模拟用户操作。
- 这些动作和效果可以正常测试。
- 您将需要一个对模态表单非常特殊的附加测试:
- 模态表单通常通过设置模态结果来关闭。
- 您使用的事实
Show
意味着不会通过设置模态结果来关闭表单。
- 这很好,因为如果您现在模拟单击“确定”按钮...
- 您可以简单地检查
ModalResult
是否正确。
警告
您可以使用此技术通过以非模态方式显式显示特定的模态表单来测试它。但是,任何显示模式形式的测试代码(例如错误对话框)都会暂停您的测试用例。
即使是您的示例代码:也会Click ('OpenConfigButton');
导致 ShowModal 被调用,并且无法以这种方式进行测试。
要解决此问题,您需要将“显示命令”注入到您的应用程序中。如果您不熟悉依赖注入,我推荐您在 YouTube 上观看 Misko Hevery 的 Clean Code Talks 视频。然后在测试时,你注入一个合适的“显示命令”版本,它不会显示模式形式。
例如,如果单击 Ok 按钮时验证失败,您的模态表单可能会显示错误对话框。
所以:
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;