К основному контенту

Тестируем с помощью 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 обсуждаются :) Подробней о них в следующем посте.

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

Продолжение

Комментарии

  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".

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

Отправка комментария

Популярные сообщения из этого блога

Mock vs Stub

Когда мы начали изучать модульное тестирование, то одними из первых терминов, с которыми пришлось познакомиться, стали Mock и Stub.

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

Проверять работоспособность тестируемого объекта (system uder test - SUT) можно двумя способами: оценивая состояние объекта или его поведение.

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

Во-втором, мы проверяем набор и порядок действий (вызовов методов взаимодействующих объектов, других методов SUT), которое должен совершить метод SUT.

Собственно, если коротко, то в одном случае используется Stub, а в другом Mock. Это объекты, которые создаются и используются взамен реальных объектов, с которым взаимодействует SUT в процессе своей работы.

Теперь подробнее.

Gerard Meszaros использует термин Test Double (дублер), как обозначение для объекта, который зам…

План "Б" или как прикольно провести субботний день

Всем привет.
Вчера состоялась конференция "План Б". Организаторами выступили ребята из Яндекса, за что им большое спасибо. Судя по приблизительным подсчетам в мероприятии участвовало около 200 человек.

Основной темой конференции было планирование, планирование всего: проектов, разработки, тестирования, дизайнеров и даже организации музыкального фестиваля.
Сначала думал написать отчет в обычном своем стиле: кто и что говорил, но почитав твиттер по #pbconf понял, что просто потеряю время :) Поэтому кому оооочень интересно узнать подробности следуйте за птичкой и вы все узнаете (тэг #pbconf попал в top-30 твиттера)
Здесь приведу лишь те вещи, которые мне запали в мозг
Роман Чернин о продуктовой разработке: "нет заказчика, нет требований, нет сроков -> как принимать решения? ответ: заводим себе Product Manager-а"
Оля Павлова (@op): "бойтесь иллюзии точной формулировки" "заказчик - ребенок, выдаем ему игрушку как можно чаще" "не забываем, …

Полезные ресурсы для молодых (и не только) тестировщиков

Уже 3 месяца провожу собеседования тестировщиков.
Поначалу они просто  веселили - после 15-летнего опыта собеседования С++-разработчиков, общение с тестировщиками (чаще были "-цы") было чем то экзотическим и забавным.

Потом становилось все грустнее и грустнее, мимими закончилось. Началась печаль.