我正在测试用 CEF4Delphi 创建的一些进程通过 DUnit 在我的应用程序中。
以下是重现该问题的 MCVE:
unit MyUnit;
interface
{$I cef.inc}
uses
Winapi.Windows,
Winapi.Messages,
System.SysUtils,
System.Variants,
System.Classes,
Vcl.Graphics,
Vcl.Controls,
Vcl.Forms,
Vcl.Dialogs,
uCEFWindowParent,
uCEFChromiumWindow,
uCEFChromium,
Vcl.ExtCtrls,
Vcl.StdCtrls;
type
TForm1 = class(TForm)
ChromiumWindow1: TChromiumWindow;
Timer1: TTimer;
procedure Timer1Timer(Sender: TObject);
procedure FormShow(Sender: TObject);
procedure ChromiumWindow1AfterCreated(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
FChromiumCreated: Boolean;
procedure WMMove(var aMessage: TWMMove); message WM_MOVE;
procedure WMMoving(var aMessage: TMessage); message WM_MOVING;
public
{ Public declarations }
function IsChromiumCreated: Boolean;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.ChromiumWindow1AfterCreated(Sender: TObject);
begin
ChromiumWindow1.LoadURL('https://www.google.com');
FChromiumCreated := True;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
FChromiumCreated := False;
end;
procedure TForm1.FormShow(Sender: TObject);
begin
if not (ChromiumWindow1.CreateBrowser) then
Timer1.Enabled := True;
end;
function TForm1.IsChromiumCreated: Boolean;
begin
Result := FChromiumCreated;
end;
procedure TForm1.Timer1Timer(Sender: TObject);
begin
Timer1.Enabled := False;
if not (ChromiumWindow1.CreateBrowser) and not (ChromiumWindow1.Initialized) then
Timer1.Enabled := True
end;
procedure TForm1.WMMove(var aMessage: TWMMove);
begin
inherited;
if (ChromiumWindow1 <> nil) then
ChromiumWindow1.NotifyMoveOrResizeStarted;
end;
procedure TForm1.WMMoving(var aMessage: TMessage);
begin
inherited;
if (ChromiumWindow1 <> nil) then
ChromiumWindow1.NotifyMoveOrResizeStarted;
end;
end.
以下是测试用例:
unit TestMyTest;
{
Delphi DUnit Test Case
----------------------
This unit contains a skeleton test case class generated by the Test Case Wizard.
Modify the generated code to correctly setup and call the methods from the unit
being tested.
}
interface
uses
TestFramework,
Vcl.Forms,
MyUnit,
System.Classes;
type
// Test methods for class TForm1
TestTForm1 = class(TTestCase)
strict private
FFormHolder: TForm;
FForm1: TForm1;
public
procedure SetUp; override;
procedure TearDown; override;
published
procedure TestFormActivate;
end;
implementation
procedure TestTForm1.SetUp;
begin
Application.Initialize;
FForm1 := TForm1.Create(nil);
Application.Run;
end;
procedure TestTForm1.TearDown;
begin
FForm1.Free;
FForm1 := nil;
end;
procedure TestTForm1.TestFormActivate;
begin
FForm1.Show;
CheckTrue(FForm1.IsChromiumCreated);
end;
initialization
// Register any test cases with the test runner
RegisterTest(TestTForm1.Suite);
end.
如果我使用 .Show,指令 FChromiumCreated := True;
不会执行,TChromium 不会加载页面并且测试返回 false。
我不确定,但这可能是因为 TChromium 是异步初始化的,并且执行测试时 TChromium 尚未完全初始化。
在这种情况下如何进行测试?
编辑 我已阅读this answer 。就我而言,.Show 确实允许进入下一行测试,但 TChromium 似乎在该阶段尚未完全初始化。我也尝试了 tomazy 的建议,但这也不起作用。
最佳答案
您的测试不可能以当前形式通过。 Chromium 的加载是延迟的,并且只会在将来的某个时候加载。然而您的测试会立即检查它是否已加载。测试异步代码是可能的,但它确实会让你的测试变得一团糟。我提醒你要小心你正在测试的内容。您可能想要使用 Selenium 等其他工具进行页面行为测试,并将 Delphi 测试的重点放在是否在所需情况下加载正确的页面。
粗略地看一下 CEF4 演示代码就会发现创建可能被延迟的原因。
GlobalCEFApp.GlobalContextInitialized has to be TRUE before creating any browser. If it's not initialized yet, we use a simple timer to create the browser later.
警告:全局状态可能会对单元测试造成严重破坏。您需要进一步调查以确定如何最好地确保您的测试不会受到此状态的负面影响。
一种可行的方法是确保在开始运行任何测试之前初始化 GlobalCEFApp.GlobalContextInitialized
。但我怀疑这将是一个相当有限的解决方案,因为尽管我不熟悉 TChromiumWindow 组件,但我怀疑它的许多交互都是异步的。您可以触发某些内容,但随后您必须等待事件回调才能确定最终结果。
这就是你的测试代码会变得困惑的地方。例如,假设您的表单旨在在 Chromium 窗口完全初始化后自动加载特定页面。您的测试必须执行以下操作:
procedure TestTForm1.TestBrowserLoad;
begin
FForm1.InitialPage := 'https://google.com';
FForm1.Show;
WaitForChromiumCreated(Form1.ChromiumWindow1); { <-- This is the tricky bit }
CheckTrue(FForm1.IsChromiumCreated);
end;
本质上,WaitForChromiumCreated
必须允许主窗体的消息循环继续泵送消息。而且还在您的测试方法中阻止处理。它还需要可靠地知道组件何时完全初始化。在这种情况下,ProcessMessages()
是合理的,因为您无法重新构建 CEF4。
按照以下几行应该可以解决问题。
procedure WaitForChromiumCreated(AChromiumWindow: TChromiumWindow);
begin
while True do
begin
if (AChromiumWindow.Initialized) then Break;
{ You'll also need a way to break out of this loop
if something goes wrong and the component cannot
initialise, or if the tests are aborted. }
Application.ProcessMessages();
end;
end;
提示:我还强烈建议向所有 Wait...
方法添加超时参数,并使您的测试在等待时立即失败意外超时。
关于delphi - CEF4Delphi 和 DUnit,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48248267/