CEF4Delphi и DUnit
Я тестирую несколько процессов, которые я создал с помощью 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 еще не инициализирован полностью.
Как можно выполнить мой тест в этом случае?
Изменить Я прочитал этот ответ. В моем случае.Show позволяет перейти к следующей строке теста, но кажется, что TChromium не инициализировался полностью на этом этапе. Я также попробовал предложение от Томази, но это тоже не работает.
1 ответ
Нет шансов, что ваш тест пройдёт в его текущей форме. Загрузка Chromium задерживается и будет загружена только в какой-то момент в будущем. Тем не менее, ваш тест немедленно проверяет, загружен ли он. Тестирование асинхронного кода возможно, но это действительно запутывает ваши тесты. Я бы предупредил вас быть осторожным с тем, что вы тестируете. Возможно, вы захотите использовать другой инструмент, такой как Selenium, для тестов поведения страниц и сфокусировать свои тесты Delphi на том, загружаете ли вы нужные страницы в требуемых ситуациях.
Беглый взгляд на демонстрационный код CEF4 показывает, что создание может быть отложено.
GlobalCEFApp.GlobalContextInitialized должно быть ИСТИНА перед созданием любого браузера. Если он еще не инициализирован, мы используем простой таймер для создания браузера позже.
ВНИМАНИЕ: Глобальное состояние может нанести ущерб модульному тестированию. Вам нужно будет продолжить расследование, чтобы определить, как лучше всего убедиться, что это состояние не оказывает негативного влияния на ваши тесты.
Один из подходов, который может сработать, заключается в обеспечении 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...
методы и заставить ваши тесты сразу же провалиться, если время ожидания неожиданно истекло.