Всю прошедшую неделю я потихоньку занимался развитием DelphiSpec. Хотя свободного времени у меня было не так уж и много, проделана большая работа и проект близок к той стадии, когда я без стеснения смогу объявить о выходе версии 1.0.
Давайте я вам вкратце расскажу о том, что теперь умеет DelphiSpec. Базовые вещи озвучены в предыдущем посте, обсудим, что появилось нового. А нового довольно много.
Во-первых, произошла по сути косметическая, но очень приятная модернизация — в классе содержащем step definitions простые шаги можно описывать без применения атрибутов.
В первом посте я показывал вам такой синтаксис:
// пример сценария: "Given user ROOT exists"
[Given_('user (.*) exists')]
procedure MyProcName(const Name: string);
Он отлично работает и сейчас, но теперь можно сделать еще проще — без атрибутов:
// пример сценария: "Given user ROOT exists"
procedure Given_user_name_exists(const Name: string);
То есть можно использовать имя самого метода для описания типа и синтаксиса шага. Выбирайте как удобнее.
Также теперь поддерживаются не только строковые параметры. Такой код будет работать:
// пример сценария: "Given I have 3 apples"
procedure Given_I_have_N_apples(N: Integer);
Можно передать даже массив:
// пример сценария: "Given I have a list 1,2,3,4"
procedure Given_I_have_a_list_Ns(Ns: TArray<Integer>);
И даже массив записей:
type
TUserInfo = record
Name: string;
Password: string;
Id: Integer;
end;
// пример сценария
// Given users exist:
// | id | name | password |
// | 1 | Roman | pass1 |
// | 2 | Other | pass2 |
procedure Given_users_exist(Table: TArray<TUserInfo>);
Последний пример демонстрирует интересный тип данных Gherkin — data table. Обратите внимание, что такая таблица в метод-обработчик передается как массив обычных типизированных записей.
Всю работу с типами берет на себя DelphiSpec, вам об этом думать не придется. Библиотека может это делать благодаря магии RTTI. Не верьте, когда вам говорят, что волшебства не бывает :)
Еще один тип данных, который поддерживает Gherkin и теперь реализован в DelphiSpec — это «многострочные строки» в стиле Python. Такая строка обрамляется троекратными двойными кавычками сверху и снизу, а в метод-обработчик передается как обычный string:
Given I have a blacklist: """ m@mail.com 123@mail.com """
С типами данных я, кажется, закончил. Расскажу еще о паре интересных возможностей.
Добавлена поддержка блока background. Этот блок позволяет описать контекст выполнения сценариев — он выполняется перед каждым сценарием.
Feature: Accounts
Background:
Given users exist:
| id | name | password |
| 1 | Roman | pass1 |
| 2 | Other | pass2 |
Scenario: Correct Login
Given my name is "Roman"
And my password is "pass1"
When I login
Then I have access to private messages
Scenario: Incorrect Login
Given my name is "Roman"
And my password is "pass2"
When I login
Then access denied
Также теперь можно описывать не только отдельные сценарии, но и более общие структуры сценариев:
Scenario Outline: Add two numbers
Given I have entered <num1> in calculator
And I have entered <num2> in calculator
When I press Add
Then the result should be <sum> on the screen
Examples:
| num1 | num2 | sum |
| 1 | 2 | 3 |
| 4 | 5 | 9 |
| 3 | 1 | 4 |
Такая структура будет развернута в несколько сценариев — по одному на каждую строку таблицы. В дереве DUnit подобная ситуация будет выглядеть вот так:

То есть создан не один тест «Add two numbers», а три. По одному для каждого случая.
Полный код примеров лежит вместе с библиотекой на github. Как я и обещал, DelphiSpec я не забрасываю и развиваю. Впереди мне предстоит тестирование и написание хотя бы краткой документации. А там уже и 1.0 не за горами :)
1. Alex W. Lulin
30 Дек 2013 11:49 пп
Роман, это РЕАЛЬНО КРУТО.
Жду не дождусь «продаж»…
2. Alex W. Lulin
5 Янв 2014 1:05 пп
Given_user_name_exists
мне кажется, что Given должно бы в базовую аксиоматику входить, как «конструкция языка», а не как «часть прикладного API»
3. Роман Янковский
5 Янв 2014 2:06 пп
Если честно, я не понял о чем ты. Given вроде как именно конструкция языка и есть.
4. Alex W. Lulin
6 Янв 2014 12:04 дп
«Given вроде как именно конструкция языка и есть.»
Только ты зачем-то «зашиваешь» её в «прикладное API».
Может быть я опять «не донёс».