Про реальную разработку написано тут:
http://18delphi.blogspot.ru/2013/10/blog-post_7652.html
http://18delphi.blogspot.ru/2013/10/blog-post_7099.html
Теперь попробую описать собственно идею.
Пусть у нас есть форма:
Пусть мы хотим иметь возможность нажимать на кнопку SomeButton на форме SomeForm.
Из наших тестов.
Этому нам помогут следующие слова тестовой машины:
И реализация:
Регистрируем описанные слова в тестовой машине:
Теперь мы можем написать первый тест:
И он делает то, что хотелось.
(Обратная польская запись пусть вас не пугает. Пишу так - потому, что у меня так сделано. Исторически. Обратная польская запись или инфиксная - не суть важно.)
Но. Хотелось бы "по-русски".
Напишем "вспомогательные" слова. Уже на скриптовом языке тестовой машины.
Пусть мы знаем, что SomeForm, это форма для регистрации пользователя, а SomeButton это кнопка для сохранения.
Ну из бизнес-логики такое следует.
Напишем:
Тогда тест можно переписать так:
Ну отчасти "по-русски". Но - не совсем.
Пойдём дальше.
Напишем:
Тогда тест можно переписать так:
Ну и последний штрих.
Вносим написанное слово в словарь:
Теперь тест приобретает вид:
- все служебные конструкции - "упрятаны" внутрь "слова высокого уровня" - "Нажать кнопку Сохранить на форме регистрации".
И это уже не совсем "слово", а по сути - целое "предложение.
Которое попало в словарь скриптовой машины и которое скриптовая машина опознаёт как валидный идентификатор. И который начинает участвовать в процессе компиляции скриптов.
Зачем это нужно?
Ну конечно не для того, чтобы "писать по-русски". Это конечно - утопия.
Идея в том, что подобные тесты можно "читать по-русски".
Тест приобретает вид TestCase'а.
И его может выполнять не только скриптовая машина, но и человек.
Ну и - "самодокументируемость кода".
Таким образом выводя "нужные ручки" со стороны Delphi (не обязательно через RTTI) и описывая "высокоуровневые" слова в промежуточных словарях тестовой машины - можно быстро "набрать массу", необходимую для написания тестов в виде TestCase'ов.
Немножко о том "как всё это устроено внутри" читайте тут - http://18delphi.blogspot.ru/2013/11/gui_5.html
http://18delphi.blogspot.ru/2013/10/blog-post_7652.html
http://18delphi.blogspot.ru/2013/10/blog-post_7099.html
Теперь попробую описать собственно идею.
Пусть у нас есть форма:
TSomeForm = class(TForm) SomeButton : TButton; end;//TSomeForm
Пусть мы хотим иметь возможность нажимать на кнопку SomeButton на форме SomeForm.
Из наших тестов.
Этому нам помогут следующие слова тестовой машины:
interface TkwFindForm = class(TscriptKeyWord) protected procedure DoIt(aContext : TscriptContext); override; end;//TkwFindForm TkwFindComponent = class(TscriptKeyWord) protected procedure DoIt(aContext : TscriptContext); override; end;//TkwFindComponent TkwButtonClick = class(TscriptKeyWord) protected procedure DoIt(aContext : TscriptContext); override; end;//TkwButtonClick
И реализация:
implementation procedure TkwFindForm.DoIt(aContext : TscriptContext); var l_Name : String; l_Form : TForm; begin l_Name := aContext.PopString; Assert(l_Name <> ''); l_Form := Screen.FindFormByName(l_Name); // - такого метода нет, но его легко можно написать Assert(l_Form <> nil); aContext.PushObject(l_Form); end; procedure TkwFindComponent.DoIt(aContext : TscriptContext); var l_Name : String; l_Form : TForm; l_Component : TComponent; begin l_Name := aContext.PopString; l_Form := aContext.PopObject As TForm; Assert(l_Name <> ''); Assert(l_Form <> nil); l_Component := l_Form.FindComponent(l_Name); Assert(l_Component <> nil); aContext.PushObject(l_Component); end; procedure TkwButtonClick.DoIt(aContext : TscriptContext); var l_Component : TComponent; begin l_Component := aContext.PopObject As TComponent; Assert(l_Component Is TButton); (l_Component As TButton).Click; end;
Регистрируем описанные слова в тестовой машине:
initialization ScriptEngine.RegisterWord(TkwFindForm, 'FindForm'); ScriptEngine.RegisterWord(TkwFindComponent, 'FindComponent'); ScriptEngine.RegisterWord(TkwButtonClick, 'ButtonCick');Понятное, дело, что идентификаторы слов можно "придумывать" из ClassName, но это - не так важно.
Теперь мы можем написать первый тест:
VAR l_Form VAR l_Button 'SomeForm' FindForm =: l_Form l_Form 'SomeButton' FindComponent =: l_Button l_Button ButtonClick
И он делает то, что хотелось.
(Обратная польская запись пусть вас не пугает. Пишу так - потому, что у меня так сделано. Исторически. Обратная польская запись или инфиксная - не суть важно.)
Но. Хотелось бы "по-русски".
Напишем "вспомогательные" слова. Уже на скриптовом языке тестовой машины.
Пусть мы знаем, что SomeForm, это форма для регистрации пользователя, а SomeButton это кнопка для сохранения.
Ну из бизнес-логики такое следует.
Напишем:
CONST "Форма регистрации" 'SomeForm' CONST "Сохранить" 'SomeButton'
Тогда тест можно переписать так:
VAR l_Form VAR l_Button "Форма регистрации" FindForm =: l_Form l_Form "Сохранить" FindComponent =: l_Button l_Button ButtonClick
Ну отчасти "по-русски". Но - не совсем.
Пойдём дальше.
Напишем:
STRING FUNCTION "Найти форму" STRING IN aFormName aFormName FindForm =: Result END //"Найти форму" STRING FUNCTION "Найти найти компонент на форме" STRING IN aComponentName OBJECT IN aForm aForm aComponentName FindComponent =: Result END //"Найти найти компонент на форме"" PROCEDURE "Нажать кнопку" OBJECT IN aButton aButton ButtonClick END //"Нажать кнопку" WORDALIAS VAR Переменная
Тогда тест можно переписать так:
Переменная "Найденная форма" Переменная "Найденная кнопка" "Найти форму {("Форма регистрации")}" =: "Найденная форма" "Найти найти компонент {("Сохранить")} на форме {("Найденная форма")}" =: "Найденная кнопка" "Нажать кнопку {("Найденная кнопка")}"
Ну и последний штрих.
Вносим написанное слово в словарь:
PROCEDURE "Нажать кнопку Сохранить на форме регистрации" Переменная "Найденная форма" Переменная "Найденная кнопка" "Найти форму {("Форма регистрации")}" =: "Найденная форма" "Найти найти компонент {("Сохранить")} на форме {("Найденная форма")}" =: "Найденная кнопка" "Нажать кнопку {("Найденная кнопка")}" END //"Нажать кнопку Сохранить на форме регистрации"
Теперь тест приобретает вид:
"Нажать кнопку Сохранить на форме регистрации"
- все служебные конструкции - "упрятаны" внутрь "слова высокого уровня" - "Нажать кнопку Сохранить на форме регистрации".
И это уже не совсем "слово", а по сути - целое "предложение.
Которое попало в словарь скриптовой машины и которое скриптовая машина опознаёт как валидный идентификатор. И который начинает участвовать в процессе компиляции скриптов.
Зачем это нужно?
Ну конечно не для того, чтобы "писать по-русски". Это конечно - утопия.
Идея в том, что подобные тесты можно "читать по-русски".
Тест приобретает вид TestCase'а.
И его может выполнять не только скриптовая машина, но и человек.
Ну и - "самодокументируемость кода".
Таким образом выводя "нужные ручки" со стороны Delphi (не обязательно через RTTI) и описывая "высокоуровневые" слова в промежуточных словарях тестовой машины - можно быстро "набрать массу", необходимую для написания тестов в виде TestCase'ов.
Немножко о том "как всё это устроено внутри" читайте тут - http://18delphi.blogspot.ru/2013/11/gui_5.html
А как Вы можете классифицировать созданный язык для тестовой машины? :)
ОтветитьУдалитьНу типа студентам объяснять терминологически грамотно, что Александр наваял.
Императивный язык
ОтветитьУдалитьПравда с лямбдами. Но это для современных императивных языков - не новость.
ОтветитьУдалитьПри создании этого скриптового языка мы изначально не преследовали цель попасть в какую-то классификацию согласно академическим традициям. Хотелось, чтобы язык развивался естественным образом, через практику применения простыми пользователями, которые и пишут кейсы. Можно сказать, что возможности языка здесь не рафинированные, а синтаксис выкристаллизовывался поэтапно.
ОтветитьУдалитьСейчас можно, конечно, спекулировать на критике такого подхода, но придумать что-то более удобное и, главное, эффективное сложно. Если вы посмотрите на готовые "тест-машины", то в принципе они все похожи.
Ну а так - это, да, императивный язык :) Но обозвать язык "императивным", не понимая, как он при этом реализуется - от этого не будет пользы студентам. Хотя сам по себе данный пример весьма показателен.
Скажите, как у вас решается (планируете решать) проблема несоответствия терминологии языка тестов и языка предметной области? Например, тестеру нужно проверить как заполняется какой-нибудь справочник. Он вместо того, что бы написать в скрипте "заполнить справочник Х такими-то данными" пишет что-то типа "Открыть пункт меню такой-то, нажать на пункт меню такой-то и т.д.", получается, что тестеры должны знать архитектуру приложения. По опыту это довольно трудоемко, мы в свое время от такого тестирования отказались, руководство не поняло в чем смысл таких трудозатрат, поэтому сейчас тестируем по старинке ручками.
ОтветитьУдалитьТак о том и речь, что тестер должен оперировать ПОНЯТИЯМИ СИСТЕМЫ и ПРЕДМЕТНОЙ области. К этому и стремимся.
ОтветитьУдалитьПросто у нас "меню" - это "предметная область". В некотором роде.
А вообще говоря - никто не мешает сделать слово "заполнить справочник {(Х)} такими-то {(данными)}"
Нет никаких препятствие для реализации этого.
Ну с учётом "ручек" со стороны Delphi и исходного приложения.
Да! В приложение надо ВСТРАИВАТЬ код для скриптов. Аксиоматику. Под IfDef. Но как показывает практика - это НЕ МЕШАЕТ.
Тот же TestComplete - тоже встраивает специальный "тестовый функционал".
TestComplete - http://smartbear.com/products/qa-tools/automated-testing-tools/
ОтветитьУдалить