Этим постом начну новую серию цель которой помогать на начальном этапе. Я думаю раз в две недели каждый разработчик может написать нечто подобное, потому как приблизительно за это время у него набирается экспертизы достаточно, для того, чтобы позволить ускориться его коллегам. Так и со мной - около двух недель назад я получил конкретное задание - и теперь сейчас я могу написать этот пост про замечательную разработку Fitnesse. Вперед.
К чему (в частности) стремятся Agile проекты? Минимум документации в пользу самотестирующегося и самодокументирующегося кода. А все потому, что документацию надо поддерживать наряду с кодом. Итак, если есть тесты, которые тестируют систему, а еще они написаны понятным языком, то это хорошо. Но вместе с тем в больших проектах от документации не особо и уходят. Ну и неудивительно - документация описывает систему на достаточно абстрактном уровне (бизнеса), а тесты они все же сильно привязаны к интерфейсам модулей (понятным только программистам). Заказчику легче работать с тестами первого типа, да и вообще, с не с тестами, а с текстовыми документами, графиками, табличками. Код же для программистов. И возникает вопрос, как тут избавиться от поддержки двух версий описания системы (код и документ с таблицами)? АА выход придумали и называется он Fitnesse.
Рассмотрим жизненный цикл документа в команде, которая работает с заказчиком с помощью Fitnesse. Все начинается с того, что у заказчика загорается лампочка и он начинает описывать команде разработчиков, как должна работать будущая система. Вначале тексту дает, позже у программистов появляются наводящие вопросы и чаще всего графики, таблички, блок-схемы...
Давайте рассмотрим пример подобного требования на примере некого устройства.
Вот такой такой вот документ может случиться в результате общения команды разработчиков с заказчиком. К чему (в частности) стремятся Agile проекты? Минимум документации в пользу самотестирующегося и самодокументирующегося кода. А все потому, что документацию надо поддерживать наряду с кодом. Итак, если есть тесты, которые тестируют систему, а еще они написаны понятным языком, то это хорошо. Но вместе с тем в больших проектах от документации не особо и уходят. Ну и неудивительно - документация описывает систему на достаточно абстрактном уровне (бизнеса), а тесты они все же сильно привязаны к интерфейсам модулей (понятным только программистам). Заказчику легче работать с тестами первого типа, да и вообще, с не с тестами, а с текстовыми документами, графиками, табличками. Код же для программистов. И возникает вопрос, как тут избавиться от поддержки двух версий описания системы (код и документ с таблицами)? АА выход придумали и называется он Fitnesse.
Рассмотрим жизненный цикл документа в команде, которая работает с заказчиком с помощью Fitnesse. Все начинается с того, что у заказчика загорается лампочка и он начинает описывать команде разработчиков, как должна работать будущая система. Вначале тексту дает, позже у программистов появляются наводящие вопросы и чаще всего графики, таблички, блок-схемы...
Давайте рассмотрим пример подобного требования на примере некого устройства.
Необходимо смоделировать устройство, позволяющее производить арифметические вычисления. Устройство состоит из кнопок «+» «–» «=», цифровой клавиатуры с кнопкой «.» и кнопки «с» (сброс). Нажатие на каждую кнопку считается операцией.
Сброс состояния устройства
Операция | Показания экрана после выполнения операции |
C | 0 |
1 | 1 |
C | 0 |
Ввод числа в устройство
Операция | Показания экрана после выполнения операции |
C | 0 |
1 | 1 |
2 | 12 |
5 | 125 |
7 | 1257 |
8 | 12579 |
Ввод дробного числа
Операция | Показания экрана после выполнения операции |
C | 0 |
5 | 5 |
6 | 56 |
7 | 567 |
. | 567. |
1 | 567.1 |
2 | 567.12 |
Далее, в колонке «Операция» (для удобства) допустимо упоминание нескольких операций идущих подряд, например.
Операция | Показания экрана после выполнения операции |
C123.567 | 123.567 |
что равносильно
Операция | Показания экрана после выполнения операции |
C | 0 |
1 | 1 |
2 | 12 |
3 | 123 |
. | 123. |
5 | 123.5 |
6 | 123.56 |
7 | 123.567 |
Операция суммирования
Суммирование - это операция, которая работает так
Операнд 1 | Операнд 2 | Результат |
123 | 456 | 579 |
45 | 1 | 46 |
0 | 1 | 1 |
10 | 1200 | 1210 |
555 | 777 | 1132 |
Суммирование двух чисел
Операция | Показания экрана после выполнения операции |
C123 | 123 |
+ | 123 |
456 | 456 |
= | 579 |
Суммирование более двух чисел
Операция | Показания экрана после выполнения операции |
C123 | 123 |
+ | 123 |
456 | 456 |
+ | 579 |
567 | 567 |
+ | 1146 |
111 | 111 |
= | 1257 |
Операция вычитание
Вычитание - это операция, которая работает так
Операнд 1 | Операнд 2 | Результат |
222 | 111 | 111 |
333 | 3 | 330 |
0 | 1 | -1 |
23 | 25 | -2 |
57 | 57 | 0 |
Вычитание двух и более чисел работает аналогично суммированию
Операция | Показания экрана после выполнения операции |
C123 | 123 |
- | 123 |
456 | 456 |
= | -333 |
Операция | Показания экрана после выполнения операции |
C123 | 123 |
– | 123 |
456 | 456 |
– | -333 |
567 | 567 |
– | -900 |
111 | 111 |
= | -1011 |
Можно комбинировать суммирование и вычитание
Операция | Показания экрана после выполнения операции |
C123 | 123 |
– | 123 |
456 | 456 |
+ | -333 |
567 | 567 |
= | 234 |
Отрицательное число в качестве первого операнда
Операция | Показания экрана после выполнения операции |
C | 0 |
–123 | 123 |
+ | -123 |
124 | 124 |
= | 1 |
После того, как документ есть у команды на руках - его анализируют на наличие сходных табличек. В данном документе есть два вида табличек - таблички первого типа определяют, как работать с устройством, а остальные - дают пояснения к операциям. Цель этого этапа - вывести и описать все таблички, которые встречаются в терминологии заказчика. Если таблички не существенно отличаются, то можно их обобщить. Если встречаются графики, то их стоит представить в виде таблиц.
После того, как на руках имеются описания таблиц - можно приступить к реализации так называемых фикстур. Что это такое - станет ясно дальше. А пока мы скачаем Fitnesse.
Архив распакуем в какую-то папку в корне (в принципе не важно) диска и осмотримся внутри
Нас интересует файл run.bat запускающий сервер. Создадим рядом еще один bat файл с таким содержимым.
run -p 8085
В результате запуска этого файла мы увидим консоль с текстом
D:\fitnesse>run -p 8085 D:\fitnesse>java -Xmx100M -jar fitnesse.jar -p 8085 FitNesse (v20090321) Started... port: 8085 root page: fitnesse.wiki.FileSystemPage at ./FitNesseRoot logger: none authenticator: fitnesse.authentication.PromiscuousAuthenticator html page factory: fitnesse.html.HtmlPageFactory page version expiration set to 14 days.
Это значит - все прошло успешно и мы можем запустить в своем браузере localhost:8085
По этому сайту можно походить, а когда надоест то в адресной строке браузера введи http://localhost:8085/CalculatorTest
Т.к. это википодобная система, то тебе предложат создать новую страничку, внутренность которой наполни содержимым документа заказчика. Строчку "!contents -R2 -g -p -f -h" моно удалить. Напомню так же, что таблицы в wiki коде строятся с помощью символа "|".
Вот код из документа, конвенртированный в wiki формат.
Необходимо смоделировать устройство, позволяющее производить арифметические вычисления. Устройство состоит из кнопок «+» «–» «=», цифровой клавиатуры с кнопкой «.» и кнопки «с» (сброс). Нажатие на каждую кнопку считается операцией. Сброс состояния устройства |Операция|Показания экрана после выполнения операции| |C|0| |1|1| |C|0| Ввод числа в устройство |Операция|Показания экрана после выполнения операции| |C|0| |1|1| |2|12| |5|125| |7|1257| |8|12579| Ввод дробного числа |Операция|Показания экрана после выполнения операции| |C|0| |5|5| |6|56| |7|567| |.|567.| |1|567.1| |2|567.12| Далее, в колонке «Операция» (для удобства) допустимо упоминание нескольких операций идущих подряд, например. |Операция|Показания экрана после выполнения операции| |с123.567|123.567| что равносильно |Операция|Показания экрана после выполнения операции| |C|0| |1|1| |2|12| |3|123| |.|123.| |5|123.5| |6|123.56| |7|123.567| Операция суммирования Суммирование - это операция, которая работает так |Операнд 1|Операнд 2|Результат| |123|456|579| |45|1|46| |0|1|1| |10|1200|1210| |555|777|1132| Суммирование двух чисел |Операция|Показания экрана после выполнения операции| |C123|123| |+|123| |456|456| |=|579| Суммирование более двух чисел |Операция|Показания экрана после выполнения операции| |C123|123| |+|123| |456|456| |+|579| |567|567| |+|1146| |111|111| |=|1257| Операция вычитание Вычитание - это операция, которая работает так |Операнд 1|Операнд 2|Результат| |222|111|111| |333|3|330| |0|1|-1| |23|25|-2| |57|57|0| Вычитание двух и более чисел Работает аналогично суммированию |Операция|Показания экрана после выполнения операции| |C123|123| |-|123| |456|456| |=|-333| |Операция|Показания экрана после выполнения операции| |C123|123| |–|123| |456|456| |–|-333| |567|567| |–|-900| |111|111| |=|-1011| Комбинирование операций |Операция|Показания экрана после выполнения операции| |C123|123| |–|123| |456|456| |+|-333| |567|567| |=|234| Отрицательное число в качестве первого операнда |Операция|Показания экрана после выполнения операции| |C|0| |–123|123| |+|-123| |124|124| |=|1|
А вот как выглядит это все после нажатия кнопки Save. Впрочем, ничего необычного - это делает wiki.
Документ как документ, но нажмем кнопку Test на панели слева.
Что случилось? А случилось вот что - мы попробовали запустить тест, описанный в этом документе. Как такое возможно? А просто - каждая табличка представляет собой проверку и называется fixture (фикстура). Как мы определили раньше - в нашем документе-тесте два вида таблиц-фикстур. Пока фикстуры наши не совсем рабочие, о чем свидетельствует окраска таблиц в желтый цвет с вклиненным сообщением "Could not find fixture: Операция." Чтобы таблички-фикстуры заработали - надо описать java класс, обрабатывающий данные описанные в табличке-фикстуре документа-теста. Тут как раз и начинается третий этап обработки требований заказчика командой, работающей с использованием Fitnesse.
Создадим новый java проект в вашей любимой IDE (я юзаю Eclipse). К этому времени у тебя должна стоять виртуальная машина java и эта самая IDE.
Приличия ради я создам пакет "calculator.fixtures", в котором будут размещены все обе фикстуры.
Далее создадим папочку lib и скопируем туда библиотеку fitnesse.jar из папки с установленным fitnesse.
Эту библиотеку добавим к проекту.
Дальше создадим таких два класса
package calculator.fixtures; import fit.Fixture; public class CalculatorActionFixture extends Fixture { }
package calculator.fixtures; import fit.Fixture; public class CalculatorOperationFixture extends Fixture { }
Вернемся к шанему документу-тесту, нажмем кнопку "Edit" и вставим первой строчкой путь к папке с компилированными java классами проекта.
У меня это будет строчка "!path I:\MyFirstFitnesseFixtures\bin"
После сохранения в доке-тесте можно будет увидеть
Кстати так же можно указать и ссылку на jar файл, содержащий фикстуры к примеру так "!path I:\MyFirstFitnesseFixtures\calculator.jar", но в разработке я решил пропустить этап сборки в jar файл по причине экономии времени.
Дальше нам следует уточнить наши таблички-фикстуры, чтобы они ссылались каждая на свой java класс. Вот wiki код двух разных табличек после модицикации.
!|calculator.fixtures.CalculatorActionFixture| |Операция|Показания экрана после выполнения операции| |C|0| |1|1| |C|0| !|calculator.fixtures.CalculatorActionFixture| |Операция|Показания экрана после выполнения операции| |C|0| |1|1| |2|12| |5|125| |7|1257| |8|12579|
Как ты заметил, мы всего лишь добавили имя класса и пакет, в котором он размещен перед каждой таблицей. Формат строки
!|path.ClassName|Вот то, что мы увидим в результате сохранения
Если теперь нажмем на test, то увидим как таблички посерели.
Это значит, что фикстура отработана, но данные таблицы проигнорены. Еще бы - классы фикстур у нас пустые совсем...
Базовый класс fit.Fixture содержит некоторое число методов, которые можно переопределить в наследниках. Сейчас нас интересует метод doRow а так же конструктор. Переопределим их.
package calculator.fixtures; import fit.Fixture; import fit.Parse; public class CalculatorActionFixture extends Fixture { public CalculatorActionFixture() { super(); } @Override public void doRow(Parse row) { super.doRow(row); } }
Поставим внутри методов по brеakpoint.
Теперь нам надо сделать возможным debug фикстур. Делается это так. Так же, как мы прописывали где находятся java class файлы пропишем строчку
!define COMMAND_PATTERN {java -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=1044 -cp %p %m}
дальше настроим remote application debug в IDE, но пока запускать не будем
Теперь нажмем кнопку "Test" на документе-тесте
На этот раз выполнение тестов остановится в самой начальной фазе и не завершится, пока мы не подключим debugger IDE
После подключения IDE остановится в нашем breakpint
Поиграся немного.
Можно заметить, что конуструктор вызывается непосредственно перед каждой строкой
!|calculator.fixtures.CalculatorActionFixture|
А метод doRow для каждой последующей строчки таблицы.
Это FIT чудит. FIT - это фреймворк, который парсит HTML страничку, полученную из вики кода и встречая там табличку находит в сответствиее ей java класс наследник Fixture, который и запускает специальным образом - по одному экземпляру на одну таблицу, по одному методу doRow на <tr>...</td> пару тегов.
Как я говорил раньше есть и другие методы базового класса Fixture, которые можно переопределить. К примеру, метод doCell выполняется для каждой пары <td>...</td> в пределах одной row. Вот его сигнатура
public void doCell(Parse cell, int columnNumber) { super.doCell(cell, columnNumber); }А вот список всех остльных методов, как по мне - поле для экспериментов!
Если заглянуть вовнутрь параметра row метода doRow то можно увидеть его непростую структуру
Но на самом деле все просто - для таблички
Полезные значения будут такими:
первая колонка row.parts.body = 'Операция' row.parts.more.body = 'Показания экрана после выполнения операции' следующая колонка row.more.parts.body = 'C' row.more.parts.more.body = '0' следующая колонка row.more.more.parts.body = '1' row.more.more.parts.more.body = '1' следующая колонка row.more.more.more.parts.body = '2' row.more.more.more.parts.more.body = '12'
и так далее. Самое неприятное в написании фикстуры с нуля (а можно воспользоваться одним из подготовленных наследников Fixture, но об этом позже) - это то, что придется барахтаться среди этих more.more.more... Но ничего!
А вот пример того, как получается обратная связь
@Override public void doRow(Parse row) { Parse cell = row.parts; for (int i = 0; cell != null; i++, cell = cell.more) { if (i%2 == 0) { cell.addToTag(" class=\"pass\""); cell.addToBody(Fixture.label("OK")); } else { cell.addToTag(" class=\"fail\""); cell.addToBody(Fixture.label("ERR")); } } super.doRow(row); }
и вот что в результате запуска теста случится с табличкой
Что из этого эксперимента можно было извлечь? Да то, что при запуске тестов фикстура не только отрабатывает каждую табличку, но и служит генератором таблички, которая отрисуется на место исходной.
На этом низкуровневое программирование заканчиваем. Дальше я расскажу про набор классов-наследников Fixture, которые позволяют отвлечься от row.more.more.more.parts.more.body...
Но это в следующий раз...
sgs
ОтветитьУдалитьспасибо, помогло)
ОтветитьУдалить