Тестирование Delphi GUI и модальные формы - PullRequest
13 голосов
/ 04 июля 2011

В этом интересном сообщении в блоге на delphiXtreme я читал о встроенных возможностях тестирования GUI в DUnit (в основном альтернативный класс тестовых примеров TGUITestCase, определенный в модуле GUITesting, который имеет несколько служебных функций для вызова действий в графическом интерфейсе). Я был вполне доволен этим, пока не заметил, что он не работает с модальными формами. Например, следующая последовательность не будет работать, если на первой кнопке отображается форма модальной конфигурации:

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

Вторая Click выполняется только тогда, когда модальная форма закрыта, что я должен сделать вручную.

Я не знаю много о том, как модальные формы работают в фоновом режиме, но должен быть какой-то способ обойти это поведение. Наивно, я хочу как-то выполнить ShowModal "в потоке", чтобы "основной поток" оставался отзывчивым. Теперь я знаю, что запуск ShowModal в потоке, вероятно, испортит все. Есть ли альтернативы? любой способ обойти блокирующую природу ShowModal? У кого-нибудь есть опыт тестирования GUI в Delphi?

Я знаю о внешних инструментах (от QA или других), и мы используем эти инструменты, но этот вопрос касается тестирования GUI в IDE.

Спасибо!

Ответы [ 2 ]

22 голосов
/ 04 июля 2011

Вы не можете протестировать модальные формы, вызвав ShowModal;потому что, как вы совершенно правильно обнаружили, это приводит к тому, что код вашего тестового примера «приостанавливается», в то время как модальная форма ожидает взаимодействия с пользователем.

Причина этого в том, что ShowModal переключает вас во «вторичный цикл сообщений»это не завершится, пока форма не закроется.

Однако модальные формы все еще можно протестировать.

  1. Показать обычно модальную форму с использованием нормального Show метод.
  2. Это позволяет продолжить выполнение кода тестового примера и имитировать действия пользователя.
  3. Эти действия и эффекты можно протестировать как обычно.
  4. Вам понадобится дополнительный тест, весьма специфичный для модальных форм:
    1. Модальная форма обычно закрывается установкой модального результата.
    2. Тот факт, что вы использовали Show означает формуне будет закрыто путем установки модального результата.
    3. Что хорошо, потому что если вы теперь имитируете нажатие кнопки «ОК» ...
    4. Вы можете просто проверить, что ModalResultправильно.

ПРЕДУПРЕЖДЕНИЕ

Вы можете использовать эту технику для проверки конкретной модальной формы, явно показав ее немодально.Однако любой тестируемый код, который показывает модальную форму (например, Error Dialog), приостановит ваш тестовый пример.

Даже ваш пример кода: Click ('OpenConfigButton'); приводит к вызову ShowModal и не может быть протестирован таким образом.

Чтобы решить эту проблему, вам нужно, чтобы ваши «команды показа» были инъекционными в ваше приложение.Если вы не знакомы с внедрением зависимостей, я рекомендую видеоролики «Чистые разговоры по коду» Misko Hevery, доступные на You Tube.Затем во время тестирования вы вводите подходящую версию ваших «команд показа», которая не будет отображать модальную форму.

Например, ваша модальная форма может отображать диалоговое окно с ошибкой, если проверка не удалась при нажатии кнопки 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;
11 голосов
/ 11 августа 2011

На самом деле в Delphi есть способ протестировать модальные окна.Когда отображается модальное окно, ваше приложение по-прежнему обрабатывает сообщения Windows, поэтому вы можете отправить сообщение в какое-то вспомогательное окно непосредственно перед показом модального окна.Тогда ваше сообщение будет обработано из модального цикла, что позволит вам выполнить код, пока модальное окно все еще видимо.

Недавно я работал над простой библиотекой, чтобы решить эту проблему.Вы можете скачать код здесь: 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;
...