Kiedyś w jednym z komentarzu ktoś zasugerował, aby opisać również zdarzenia (hooks) w specflow. Wszystko można znaleźć w dokumentacji, ale również preferuję kilka zwięzłych przykładów niż suchy opis.
Tak jak w nUnit czy jakimkolwiek innym framework’u, możemy definiować co powinno wykonać się przed lub po testach. Atrybutami BeforeTestRun oraz AfterTestRun dekorujemy metody, które mają się wykonać przed i po wszystkich testach:
[Binding] public class Hooks { [BeforeTestRun] public static void BeforeTestRun() { } [AfterTestRun] public static void AfterTestRun() { } }
Atrybut Binding jest niezbędny, gdy definiujemy zdarzenia w osobnej klasie. W przypadku BeforeTestRun ma to sens, ponieważ nie są one specyficzne dla żadnego zestawu testów. Jeśli mamy 10 scenariuszy, BeforeTestRun oraz AfterTestRun wykonają się tylko raz – przed i po wykonaniu wszystkich testów (a nie po każdym scenariuszu osobno). Metoda powinna być również statyczna.
Jeśli mamy logikę, która powinna być wykonana dla każdego scenariusza osobno, wtedy korzystamy z BeforeScenario oraz AfterScenario:
[Binding] public class Hooks { [BeforeFeature] public static void BeforeFeature() { } [AfterFeature()] public static void AfterFeature() { } }
Dla 10 scenariuszy, metody zatem zostaną wykonane również po 10 razy. Z poprzedniego posta, pamiętamy, że mamy do dyspozycji jeszcze tagi:
@myFirstTag Feature: EditPost Jakis opis tutaj... Scenario: Edit a post Given I have logged into CMS When I press the edit a post button Then article should be updated.
W takim scenariuszu, możliwe jest powiązanie hook’a ze specyficznym tagiem:
[BeforeFeature("myFirstTag")] public static void BeforeFeature() { }
Parametr to lista tagów więc można stworzyć jeden hook dla kilku scenariuszy jednocześnie.
Before i After to aliasy dla BeforeScenario oraz AfterScenario. Moim zdaniem jednak, jeśli ktoś nie jest obeznany z SpecFlow, wtedy nazwa Before może być mało dokładna. Tak czy inaczej, odpowiednik powyższego kodu to:
[Binding] public class Hooks { [Before("myFirstTag")] public static void BeforeFeature() { } [After()] public static void AfterFeature() { } }
Jeśli chcemy wykonać kod pomiędzy Given, When, Then wtedy:
[Binding] public class Hooks { [BeforeScenarioBlock] public static void BeforeScenarioBlock() { } [AfterScenarioBlock] public static void AfterScenarioBlock() { } }
Atrybuty również wspierają tagi. Nazwa Block może być trochę myląca, ale chodzi tutaj, jak wspomniałem o GWT.
Jeśli potrzebujemy bardziej szczegółowe zdarzenia, możemy:
[Binding] public class Hooks { [BeforeStep] public static void BeforeStep() { } [AfterStep] public static void AfterStep() { } }
Pozostaje wyjaśnić czym różni się krok od bloku. Załóżmy, że mamy zaprezentowany wcześniej test:
@myFirstTag Feature: EditPost Jakis opis tutaj... Scenario: Edit a post Given I have logged into CMS When I press the edit a post button Then article should be updated.
W tym przypadku, blok i krok stanowią to samo i można to byłoby rozdzielić w następujący sposób:
[Blok 1, Krok 1] Given I have logged into CMS
[Blok 2, Krok 2] When I press the edit a post button
[Blok 3, Krok 3] Then article should be updated.
Różnica jest, gdy mamy bloki złożone z And, tzn.:
Feature: EditPost ... Scenario: Edit a post Given I have logged into CMS And I have enough permissions When I press the edit a post button Then article should be updated and afafa
Given składa się z dwóch kroków, zatem można byłoby to rozpisać:
[Blok 1, Krok 1] Given I have logged into CMS
[Krok 2] And I have enough permissions
[Blok 2, Krok 3] When I press the edit a post button
[Blok 3, Krok 4] Then article should be updated and afafa