среда, 20 февраля 2013 г.

Тестируем с помощью Fitnesse+PowerSlim. Часть 2. База.

Часть 1. Введение 
Часть 2. База (эта статья)
Часть 3. Интересные возможности
Часть 4. Демо FitNesse + Jenkins
Часть 5. Пример трансформации PowerShell скрипта в тест
Плагин для sublime, который подсвечивает синтаксис теста на Fitnesse+PowerSlim

Надеюсь, что вы нашли время (или желание?) посмотреть примеры, которые можно найти в пакете PowerSlim. Если вы их не видели, ничего страшного: все равно с нуля там ничего не понятно. Ну или почти ничего (да, Костя?) :)

Давайте попробуем разобраться, как использовать PowerSlim с пользой. Но тут нам придется углубиться чуток в теорию и разбираться на примерах.

Начнем, пожалуй, с организации тестов.

В Fitnesse есть три типа страниц: статические, сюиты и тестовые. Статические служат просто для размещения нужной информации и, например, страницы с набором ссылок с описанием фичей запланированных на релиз или примеров использования API библиотеки:


Страницы-сюиты.
Позволяют запустить тесты со всех тестовые страницы, расположенные под сюитой. Тесты при этом выполняются последовательно. Тесты с ошибками не останавливают выполнение сюиты. Отчет по результатам выполнения позволяет "ходить" по странице и смотреть подробности. Это вы могли видеть при первом запуске тестовой сюиты PowerSlim (см. первый пост). Кроме этого, есть возможность посмотреть историю выполнения тестов (Tools -> Test History):



Тестовые страницы вы уже видели и даже редактировали (если читали предыдущий пост).

Имена страниц должны содержать как минимум 2 заглавные буквы расположенные не рядом (не знаю как это описать понятней :) ). В любом случае Fitnesse (кстати правильнее FitNesse) не разрешит вам создать страницу с неправильным названием.

Как вы уже могли заметить, дерево веб-страниц на Fitnesse портале дублирует собой структуру папок под FitNesseRoot на файловой системе: папка - это имя страницы, внутри хранится ее контент (content.txt), история изменений (в *.zip файлах) и файл properties.xml со свойствами страницы, определяющими, например, ее тип. Поэтому при перемещении папок структура в web-view также меняется (по нажатию F5).



Редактировать страницы (content.txt) можно (и чаще даже проще) в удобном вам текстовом редакторе.
Страницы можно включать в другие страницы, подробнее об этом в следующем посте.

В Fitnesse есть переменные. Значение переменной наследуется всем страницами расположенным в иерархии под той, в которой она объявлена. На каждой странице переменная может быть переопределена и тогда уже новое значение будет наследоваться ниже по структуре.

Для чего они нужны? Самое простое применение, это задание имен стендов, где нужно запускать тесты, пути на файловой системе, URL и тп. Все то, что может быть определено и изменено в одном месте, а использоваться во многих.

Как же писать тесты? Для следованию правилу "трех А" (Arrange-Act-Assert) нам могут понадобиться такие операции:
  • настроить тест
  • выполнить действие, например создать, запустить, удалить что-нибудь и тп
  • проверить результат
Arrange
Откройте страницу (http://localhost:8080/PowerSlim.OriginalMode.SuiteRemoting.TestQuery) из сюиты PowerSlim


Обратите внимание на влинкованные SuiteSetUp, SetUp вверху страницы  и TearDown, SuiteTearDown внизу.
Если посмотреть исходники тестовой страницы (через "Edit"), то вы не увидите никаких намеков на включение дополнительных страниц. Их код автоматически подставляется движком Fitnesse в каждую тестовую страницу при ее выполнении. При этом запуск сюиты вызовет SuiteSetUp и SuiteTearDown только один раз на всю сюиту.
Эти специальные страницы предназначены для включения кода выполняющего общую настройку тестового окружения и зачистку после выполнения. Такая возможность помогает реализовывать изолированные и независимые друг от друга тесты.
Естественно, индивидуальную настройку среды выполнения теста все равно придется делать, но не следует забывать о выносе похожего кода в одно место (удаление дупликации).

Act
Код выполняемый для настройки (Arrange) и запуска (Act) использует одни и те же функции PowerSlim. Чаще всего используется eval. Много примеров в тестовой сюите PowerSlim.

Assert
Для проверки результатов используются вариации Query. Одиночный вызов Query ожидает, что результат будет содержать тот и только тот массив данных, который описан в тесте строчками под командой. Порядок возвращения по строчкам не важен. При использовании перед Query служебного слова subset проверяется наличие вхождения заданного тестом в итоговом массиве данных.
Интересный момент связан с тем, как определить, какие свойства-колонки мы должны задать тестом. Проще всего, для этого запустить тот PowerShell скрипт, который вы хотите использовать в PowerShell консоли (или ISE) с командой get-member (gm) через pipe '|'


Результатом будет являться набор свойств и методов, которыми обладает тип данных объектов, которые будут возвращены скриптом (жесть - но сейчас будет понятней)
PowerSlim возвращает только Property иNoteProperty объектов сложных типов.

Соответственно в тесте, мы будем использовать одно из этих свойств.


Если результат скрипта это массив строчек, то PowerSlim на его базе сгенерирует и вернет массив PSObject со свойствами Value и COMPUTERNAME. В Value будет положено значение строчки, а в COMPUTERNAME имя стенд-машины, на которой выполнялся запрос.
Смотри пример в PowerSlim.OriginalMode.SuiteRemoting.TestRemoting.

Похожим образом конвертируются массивы hashtable и dictionary. Подробнее можно посмотреть  в тестах PowerSlim:TestArrayInQueryTable, TestArrayOfNumbers, TestGenericDictionary, TestPowerShellHashTableNotation.

Кроме проверки Query, для assert'ов может использоваться команда check, которая сравнивает значение полученное во время запуска теста с ожидаемым. При этом в качестве параметров может выступать выражение. Смотри пример в PowerSlim.OriginalMode.SuiteCommon.TestScript.

Фуххх. Не знаю как вы, а я бы утомился читать столько слов. Но надеюсь, что эта информация будет полезней и понятней, когда вы посмотрите на примеры и попытаетесь написать свои первые тесты.

Да, важно. Для того чтобы Fitnesse не воспринимал PowerShell pipe символ '|' как разделитель таблицы, весь запрос надо заключать между символов !- ... -! Если при этом внутри запроса используется переменная, то ее надо вынести из этого "забора":


Я добавил немного примеров из жизни в ExampleS. Теперь, если взять свежий PowerSlim, то там это уже будет.
Также можно обратить внимание на обсуждения (1 и 2) из первого поста, там уже advanced technics обсуждаются :) Подробней о них в следующем посте.

Удачи в изучении. Главное не пугаться :)

Продолжение

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

  1. Молодец! Назначаешься главным популяризатором фитнеса в рунете. :)

    ОтветитьУдалить
  2. Классно написано! Только реально много. Предлагаю следующий коротенький пост про встроенные переменные (например PAGE_NAME) http://fitnesse.org/FitNesse.UserGuide.QuickReferenceGuide#GlobalVARIABLES.
    И про check, check not и regexp нотацию =~/pattern/

    ОтветитьУдалить
  3. ну, ты сам напросился ;) ждем от тебя пост на эту тему.

    ОтветитьУдалить
  4. Alexander Petrovskiy1 марта 2013 г., 15:32

    А какие предлагаются варианты переключения между лабами? Можно, конечно, вынести страничку с переменными выше сьюты, импортировать её, и когда надо поменять лабу, просто положить на место странички с переменными другую страничку (и перезапустить фитнес). Но это не кажется идеальным.
    Как-нибудь проще это делается?

    ОтветитьУдалить
  5. а зачем так сложно, с подменой? просто другую страницу подключить с переменными. опять же, для чего это может понадобиться?

    ОтветитьУдалить
  6. Alexander Petrovskiy1 марта 2013 г., 16:52

    Два случая: первый - это один человек написал тест, другой человек получил тест в архиве, из сорс-контрола и т.д. Тут отдельная страничка помогает, перезапуск фитнеса не нужен, код один - переменные разные.
    Второй случай - прогнал тесты на одной лабе, хочется без редактирования страниц прогнать на другой (например, одним линком - тесты в одной лабе, другим - в другой, а линк же запускается без человека).

    К примеру, абсолютно одинаковые тесты и/или сьюты надо прогнать по какому-нибудь правилу на наборе лаб.
    По сути - код один, переменные разные, но надо чтобы значения переменных менялись по правилу.

    ОтветитьУдалить
  7. Таблицы тестов пишутся в страницеX (например статической). Потом эта страница включается в testpage1 и testpage2 с разными define. Это ведь работает.

    ОтветитьУдалить
  8. Alexander Petrovskiy1 марта 2013 г., 19:05

    Я ж так и спросил - как юзать разные значения define.

    1) Редактировать переменные (а их может набежать несколько) неохота.

    2) Редактировать инклюд на страничку с другими define - да тоже неохота.

    3) Редактировать страничку как файл - неохота, потому что не знаю, как зарефрешить фитнес.

    4) Можно подключить другой фитнес и делать Edit locally - но это как п. 1, неохота :)

    5) Можно, конечно, сделать подгрзуку их откуда-то и это "откуда-то" подкладывать руками или скриптами.

    6) Есть parametrized include, но это запуск нескольких тестов или сьют с разными параметрами, а не смена параметров. Решение обратной задачи.

    7) если лаб несколько (не произвольное количество), можно создать несколько страниц с define, раздать им разные тэги, раздать эти тэги всем страницам сьюты(сьют), и тогда запускать по тэгам через линк или через SuiteQuery.



    8) можно иметь несколько страниц с define, брать через rest контент страницы, класть через rest контент страницы на страницу для define, которая импортится и сохранять страницу которая импортится.


    Получается, что опции 5, 7,8 более-менее интересные.

    ОтветитьУдалить
  9. фухх, выглядит угрожающе. У меня такого заморока и его необходимости пока не было :)

    ОтветитьУдалить
  10. Максим, а ты не думал на AutoConfeTQA (http://confetqa.ru/program-auto/) выступить с этой темой? Показать живьем, так-сказать, как за 20 минут все сделать красиво и пройти первый тест?:)

    ОтветитьУдалить
  11. Всё читаю читаю про фитнес, и никак не пойму(не найду), как мне конкретно протестировать на си шарпе написанное приложение. Есть библиотека с тестируемыми классами. Я должен для них написать еще одну библиотеку на си шарпе, которая будет предоставлять интерфейс к этим классам для фитнеса, А вот как в целом это всё запустить, не пойму, не получается. Как со стороны финтнеса писать в викиразметки тесты написано. а вот как всё это интегрировать что бы работало, не получается. Вот например после ваших статей запустил поверслим. Вроде что-то протестировал. А как это соединить с приложением не допираю ))

    ОтветитьУдалить
  12. А "приложение" - это что? Это UI, сервис, cmd-утилита? "Живет" ли весь код внутри exe-шника или он просто использует код из библиотек (dll assembly)?

    ОтветитьУдалить
  13. Странно, написал комментарий, а он пропал. Или просто еще не прошел модерацию? Я пытаюсь для начала хотя бы библиотеку протестировать. Он игнорирует тесты почему-то. Скачал последнюю версию NSlim, всё как в туториалах сделал, а не работает :( Разные версии раннера скачивал. Не пойму как устроено взаимодействие с фитнессом, потому не могу разобраться в чем ошибка. Первый запуск проходит удачно(если отладочный режим запустить) - возвращает 0. А дальше ничего не происходит.

    ОтветитьУдалить
  14. Test System: slim:C:\nfit\Runner.exe

    Вот так пишет:


    variable defined: TEST_SYSTEM=slim
    variable defined: COMMAND_PATTERN=%m -r fitSharp.Slim.Service.Runner,C:\nfit\fitsharp.dll %p
    variable defined: TEST_RUNNER=C:\nfit\Runner.exe

    classpath: C:\nfit\DigitalVideoRecorder.dll


    variable defined: COLLAPSE_SETUP=true
    variable defined: COLLAPSE_TEARDOWN=true

    importDigitalVideoRecorder Test not run
    Create Programs Test not runNameChannelDayOfWeekTimeOfDayDurationInMinutesid?House Test not run4 Test not runMonday Test not run19:00 Test not run60 Test not run$ID= Test not run

    ОтветитьУдалить
  15. А почему бы не попробовать PowerSlim? К сожалению, я ничего не могу подсказать по nslim и его раннеру. А в PowerSlim вы можете дергать методы вашей библиотеки и проверять результат. Это 100% работает, ну или во всяком случае мы попробуем разобраться почему нет, если вдруг "нет" :). PS на модерации комментов не висит.

    ОтветитьУдалить
  16. Попробовал - вот что выдает. Test Pages: 0 right, 0 wrong, 1 ignored, 0 exceptions Assertions: 0 right, 0 wrong, 0 ignored, 0 exceptions (2,659 seconds)

    А страница с тестом такая -

    !define TEST_SYSTEM {slim}

    !define COMMAND_PATTERN {PowerShell -NonInteractive -ExecutionPolicy unrestricted -file .\slim.ps1}

    !define HOST_VERSION {4.0 }

    !path C:\nfit\DigitalVideoRecorder.dll



    !define COLLAPSE_SETUP {true}

    !define COLLAPSE_TEARDOWN {true}

    !|import|

    |DigitalVideoRecorder|



    !|Create Programs |

    |Name |Channel|DayOfWeek|TimeOfDay|DurationInMinutes|id? |

    |House|4 |Monday |19:00 |60 |$ID=|

    А ошибки такие -

    slim:fitnesse.slim.SlimServiceDate:12:20:04 PM (MSK) on пятница, октября 31, 2014Test Page:.DigitalVideoRecorderCommand:PowerShell -NonInteractive -ExecutionPolicy unrestricted -file .\slim.ps1 8093Exit code:0Time elapsed:2.609 seconds

    Standard Output:

    True

    Standard Error:

    ��� ".\DigitalVideoRecorder" �� �ᯮ����� ��� ��� ���������, �㭪樨, 䠩�� �
    �ਯ� ��� �믮��塞�� �ணࠬ��. �஢���� �ࠢ��쭮��� ����ᠭ�� �����, � ⠪
    �� ����稥 � �ࠢ��쭮��� ���, ��᫥ 祣� ������ ������.
    ��ப�:1 ����:2
    + . <<<< .\DigitalVideoRecorder
    + CategoryInfo : ObjectNotFound: (.\DigitalVideoRecorder:String)
    [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

    ОтветитьУдалить
  17. Вы пытаетесь использовать Fit вместо Slim. Для того, чтобы дернуть код библиотеки из powerslim не надо реализовывать Fixture.


    Можно прямо вызывать методы assembly из powershell.
    Например в ExampleS.CreateProductShare, видно как делать функцию и как ее использовать.


    Внутри функции можно делать явную загрузку вашей реальной библиотеки и вызывать ее методы
    $Assembly = [System.Reflection.Assembly]::LoadFrom("YOUR_ASSEMBLY");
    $recorder = New-Object YouLibrary.DigitalVideoRecorder$recorder.GrabVideo($whereToSave)
    Тут вызывается реальный продакшен метод GrabVideo вашей библиотеки. Дальше вы можете проверить, что файл например записался, имеет правильный формат и тп.

    Можете прислать свои контакты мне на почту mail.ru (maxim точка shulga), можно пообщаться ближе к "телу" кода, сценариям использования и тп.

    ОтветитьУдалить
  18. Хорошо, сейчас переварю и попробую поиграться с тем, что вы написали, и уже более конкретно спрошу, если не разберусь.

    ОтветитьУдалить
  19. Очень здорово, теперь можно разработку начинать с написания не юнит тестов, а прям с викистранички с приемочными требованиями к функционалу, и плясать уже от них. :) Правда пока первые шаги, но уже большое удовлетворение ))) Хотя я себе немного по другому представлял, тут слишком уж получается низкоуровнево приходится тесты описывать. Не совсем пока понятно, где при таком построении тестов можно впихнуть реализацию общего языка, который должны понимать и разработчики и заказчики. В общем ещё надо поразмышлять )

    ОтветитьУдалить
  20. начну с того, что "общий язык, который должны понимать и разработчики, и заказчики" это прикольно, но в моей практике не работало. Скорее наоборот. Но само направление идеи правильное - разработчик может сделать кубиков, а, например, аналитик или тестировщик, может из этих кубиков истории складывать. Роль кубиков могут играть PowerSlim сценарии, внутри которых скрыт весь низкоуровневый код, а снаружи это могут быть просто "start vm" (и все равно каким образом она стартует) или "decode video" (а то что для этого делается LoadAssembly, создается объект класса и вызывается метод - это уже реализация, которая может меняться, стабироваться и тд). Снаружи это просто "action".

    ОтветитьУдалить