суббота, 9 ноября 2013 г.

GUI-тестирование "по-русски". Работа с "контролом в фокусе" и определение его класса

Отвлечёмся немного от "хардкорного программирования" и устройства скриптовой машины.

Вот тут - http://18delphi.blogspot.ru/2013/11/gui.html - я писал как искать форму и контрол по их имени

А вот тут - http://18delphi.blogspot.ru/2013/11/gui_4.html - я писал - как в TEdit ввести текст.

А вот тут - http://18delphi.blogspot.ru/2013/11/gui-rtti.html - я писал как сделать "то же самое" средствами "классического" RTTI.

Напомню, что у нас в итоге получилось:

PROCEDURE "Присвоить текст в текущий контрол" STRING IN aText
 OBJECT VAR l_Control
 FocusedControl =: l_Control
 aText l_Control EditSetText
END //"Присвоить текст в текущий контрол"

Теперь пусть мы опять же хотим поработать с контролом в фокусе, но при этом мы хотим проконтролировать, что контрол в фокусе - НУЖНОГО нам класса - TEdit или TCustomEdit, ну или наследник он него.

Ну скажем - логика теста зависит от этого.

Как это сделать?

Сейчас расскажу.

Напишем для начала два "вспомогательных" слова. 

Они - в ПРИНЦИПЕ полезны для GUI-Тестирования.

Но и ещё они помогут нам расширить наши примеры.

Вот эти слова:

OBJECT FUNCTION "Контрол в фокусе"
 FocusedControl =: Result
END // "Контрол в фокусе"

WORDWORKER "Присвоить текст в" STRING IN aText
 OBJECT VAR l_Control
 WordToWork DO =: l_Control
 aText l_Control EditSetText
END // "Присвоить текст в"

Про слово WORDWORKER я писал тут - http://18delphi.blogspot.ru/2013/11/gui_9015.html и тут - http://18delphi.blogspot.ru/2013/11/gui-2_7.html

Тогда наш пример переписывается так:

PROCEDURE "Присвоить текст в текущий контрол" STRING IN aText
 "Присвоить текст  {(aText)} в" "Контрол в фокусе"
END //"Присвоить текст в текущий контрол"

Конкатенативный язык (http://ru.wikipedia.org/wiki/%D0%9A%D0%BE%D0%BD%D0%BA%D0%B0%D1%82%D0%B5%D0%BD%D0%B0%D1%82%D0%B8%D0%B2%D0%BD%D1%8B%D0%B9_%D1%8F%D0%B7%D1%8B%D0%BA_%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F).

Не правда ли?

Теперь КАК определить, что "Контрол в фокусе" - НУЖНОГО нам класса?

А вот как:

На стороне Delphi определяем слово Inherits:

interface

type
 TkwInherits = class(TscriptKeyWord)
  protected
   procedure DoIt(aContext: TscriptContext); override;
 end;//TkwInherits

implementation

procedure TkwInherits.DoIt(aContext: TscriptContext);
var
 l_ClassName : String;

function localIsInherits(anObjectClass : TClass): Boolean;
begin
 if (anObjectClass = nil) then
  Result := false
 else
 if SameText(anObjectClass.ClassName, l_ClassName) then
  Result := true
 else
  Result := localIsInherits(anObjectClass.ClassParent);
end;
 
var
 l_Object : TObject;
begin
 l_Object := aContext.PopObject;
 Assert(l_Object <> nil);
 l_ClassName := aContext.PopString;
 aContext.PushBoolean(localIsInherits(l_Object.ClassType));
end;

initialization
 ScriptEngine.RegisterKeyWord(TkwInherits, 'Inherits');
 // - далее, если вызов RegisterKeyWord будет в виде RegisterKeyWord(TkwXXX, 'XXX'), 
 //   то я буду просто так и писать RegisterKeyWord(TkwXXX).
 //
 //   Тем более, что это у меня так и реализовано, через использование ClassName

Тогда можно написать такой код скрипта:

 'TCustomEdit' "Контрол в фокусе" Inherits IF (
  "Присвоить текст  {(aText)} в" "Контрол в фокусе"
 )

Ну или такой:

 'TCustomEdit' "Контрол в фокусе" Inherits ASSERT
 "Присвоить текст  {(aText)} в" "Контрол в фокусе"

Поработаем ещё немного.

Введём в словарь определение вот такого слова:

CONST "Строка ввода" 'TCustomEdit'

Тогда можно написать так:

 "Строка ввода" "Контрол в фокусе" Inherits IF (
  "Присвоить текст  {(aText)} в" "Контрол в фокусе"
 )

Ещё несколько штрихов.

Для начала определим слово ЯВЛЯЕТСЯ:

BOOLEAN WORDWORKERFUNCTION ЯВЛЯЕТСЯ OBJECT IN anObject
 WordToWork DO 
 // - вычисляем "параметр справа"
 anObject 
 // - anObject - "параметр слева"
 Inherits 
 // - зовём уже знакомое нам слово Inherits
 =: Result
 // - результат нашиз вычислений помещаем в результат функциии ЯВЛЯЕТСЯ
END // ЯВЛЯЕТСЯ

Тогда можно написать так:

 
 "Контрол в фокусе" ЯВЛЯЕТСЯ "Строка ввода" IF (
  "Присвоить текст  {(aText)} в" "Контрол в фокусе"
 )

Ещё определим синоним:

WordAlias "Строкой ввода" "Строка ввода"

Тогда можно написать так:

 
 "Контрол в фокусе" ЯВЛЯЕТСЯ "Строкой ввода" IF (
  "Присвоить текст  {(aText)} в" "Контрол в фокусе"
 )

Ещё немного поработаем над IF.

У нас помимо слова WORDWORKER - есть слово WORDWORKER2.

Которое умеет работать с двумя "параметрами справа".

Применим его.

Напишем следующее:

WORDWORKER2 если
 WordToWork1 DO
 // - вычислили значение "первого аргумента справа"
 IF (
  WordToWork2
  DO
  // - выполняем "второй аргумент справа"
 )
END // если

И тогда наш пример можно переписать так:

 если ( "Контрол в фокусе" ЯВЛЯЕТСЯ "Строкой ввода")
  ( "Присвоить текст  {(aText)} в" "Контрол в фокусе" )

По-русски? По-моему - по русски?

Теперь если определить слово "то":

Так:

WordAlias то NOP
// - NOP - это слово, которое ничего не делает, 
//   но может появляться там, где грамматика требует слово

Или так:

WORDWORKER то
 WordToWork DO
 // - просто вычисляем "аргумент справа"
 //   т.е. слово "то" в данном случае является proxy-словом
END // то

То мы можем переписать пример так:

если ( "Контрол в фокусе" ЯВЛЯЕТСЯ "Строкой ввода") то
  ( "Присвоить текст  {(aText)} в" "Контрол в фокусе" )

Ну и ещё введём синоним:

WordAlias Если если
// - у нас скриптовая машина - РЕГИСТРОЗАВИСИМАЯ, это - ОСОЗНАННО

Тогда можно написать так:

 Если ( "Контрол в фокусе" ЯВЛЯЕТСЯ "Строкой ввода") то
  ( "Присвоить текст  {(aText)} в" "Контрол в фокусе" )

Ну и "на закуску".

Ещё есть слово WORDWORKER2+.

Оно умеет работать с двумя фиксированными "параметрами справа".

И одним опциональным. Предварённым другим ключевым словом.

Вот пример:

WORDWORKER2+ 
 если 
  // - "если" - ОСНОВНОЕ ключевое слово. С которого начинается разбор грамматики
 иначе
 // - "иначе" - ДОПОЛНИТЕЛЬНОЕ ключевое слово. Которого может и не быть.
 WordToWork1 DO
 // - вычислили значение "первого аргумента справа"
 IF (
  WordToWork2
  DO
  // - выполняем "второй аргумент справа"
 )
 ELSE (
  WordToWork2+
  DO
  // - выполняем третий опциональный аргумент справа, если он передан"
  //   Если же он НЕ ПЕРЕДАН, то нам в аргументе WordToWork2+ - передадут уже знакомое слово NOP
 )
END // если

Тогда можно написать так:

 Если ( "Контрол в фокусе" ЯВЛЯЕТСЯ "Строкой ввода") то
  ( "Присвоить текст  {(aText)} в" "Контрол в фокусе" )
 иначе
  ( false ASSERT ) // - тут просто "упадём".

По-моему - "уже перебор информации".

Поэтому - закругляюсь.

Чуть позже я расскажу - как регистрировать РЕАЛЬНЫЕ классы объектов в тестовой машине, чтобы работать с их идентификаторами, а не со "строковыми именами".

И БОЛЕЕ ТОГО - вызывать их КОНСТРУКТОРЫ. Т.е. на самом деле - скриптовая машина - МОЖЕТ обладать возможностью СОЗДАВАТЬ объекты реального приложения.

Неплохо для "тестовой машины", а?

На самом деле - наверное самые пытливые читатели отметили, для себя, что эту скриптовую машину можно использовать не только для тестирования, но и для АВТОМАТИЗАЦИИ и написания макросов пользователя.

Такой вот VBA (http://ru.wikipedia.org/wiki/VBA) "на коленке".

Если компилировать "скриптовую обвязку" не только под IfDef Test, но и для конечного клиентского приложения.

И ДАТЬ пользователю возможность загружать и выполнять скрипты.

P.S. Я ещё вас не утомил? А то у меня план статей разросся уже до 30-ти штук.

Кому-то вообще интересно "сделать скриптовую машину своими руками" из "модели для сборки"?

Или пора закругляться?

Учитывая, что каждая новая статья приносит с собой ещё две статьи в "план для написания".

5 комментариев:

  1. Да, это круто всё! :)

    Только вот Александр, скажите мне. Правильно ли я понимаю, что у вас такая картина получается: тесты исполняются самим exe-шником?
    Ну т.е. проект может собираться в обычном режиме (типа Release) и в режиме для тестирования - в этом режиме подключается код для прогонки скриптов....

    Или всё-таки скрипты гоняются внешней программой по отношению к тестируемому проекту?

    ОтветитьУдалить
    Ответы
    1. Да! Тесты гоняются ВНУТРИ exe-шника.

      Я же писал. Под IfDef.

      Но! НИЧТО не мешает определить АКСИОМАТИКУ внутри exe-шника. А тесты - СНАРУЖИ.

      Только - АКСИОМАТИКА - внутри.

      Удалить
    2. > Я же писал
      Ну на всякий случай уточнил.

      > Тесты гоняются ВНУТРИ exe-шника
      Ну вот в этом и прелесть Вашего подхода :)

      Удалить
    3. "Ну вот в этом и прелесть Вашего подхода" - вы ПЕРВЫЙ человек, который СКАЗАЛ мне об этом. Это - ПРАВДА - ПРЕЛЕСТЬ. Я могу назвать "множество плюсов". Ну или написать статью о них.

      Удалить
    4. У нас даже название есть - InsiderTest'ы.

      Удалить