|
1 |
| -BDDfy is explained on Mehdi Khalili's blog in full details: http://www.mehdi-khalili.com/bddify-in-action/introduction |
| 1 | +BDDfy is the simplest BDD framework to use, customize and extend! The framework is explained on Mehdi Khalili's blog series in full details [here] (http://www.mehdi-khalili.com/bddify-in-action/introduction). This is a very short tutorial and quickstart. |
2 | 2 |
|
3 |
| -This is a very short tutorial and quickstart. |
| 3 | +To use BDDfy: |
4 | 4 |
|
5 |
| -BDDfy is the simplest BDD framework EVER! To use BDDfy: |
6 |
| - # Install NuGet if you have not already. |
7 |
| - # Go to 'Tools', 'Library Package Manager', and click 'Package Manager Console'. |
8 |
| - # In the console, type 'Install-Package BDDfy' and enter. |
| 5 | + - Install NuGet if you have not already. |
| 6 | + - Go to 'Tools', 'Library Package Manager', and click 'Package Manager Console'. |
| 7 | + - In the console, type 'Install-Package TestStack.BDDfy' and enter. |
9 | 8 |
|
10 |
| -This adds BDDfy assembly and its dependencies to your test project. Oh, BTW, BDDfy can work with any and all testing frameworks. In fact, it works even if you are not using any testing framework. |
| 9 | +This adds BDDfy assembly and its dependencies to your test project. If this is the first time you are using BDDfy you may want to check out the samples on NuGet. Just run Install-Package TestStack.BDDfy.Samples and it will load two fully working samples to your project. |
11 | 10 |
|
12 |
| -If this is the first time you are using BDDfy you may want to check out some of the samples on NuGet. Just search NuGet for BDDfy and you will see a list of BDDfy samples. You may install one or more samples to see how the framework works. Each sample installs required packages (including BDDfy and NUnit). |
| 11 | +A few quick facts about BDDfy: |
| 12 | + - It can run with any testing framework. Actually you don't have to use a testing framework at all. You can just apply it on your POCO (test) classes! |
| 13 | + - It does not need a separate test runner. You can use your runner of choice. For example, you can write your BDDfy tests using NUnit and run them using NUnit console or GUI runner, Resharper or TD.Net and regardless of the runner, you will get the same result. |
| 14 | + - It can run standalone scenarios. In other words, although BDDfy supports stories, you do not necessarily have to have or make up a story to use it. This is useful for developers who work in non-Agile environments but would like to get some decent testing experience. |
| 15 | + - You can use underscored or pascal or camel cased method names for your steps. |
| 16 | + - You do not have to explain your scenarios or stories or steps in string, but you can if you need full control over what gets printed into console and HTML reports. |
| 17 | + - BDDfy is very extensible: it's core barely has any logic in it and it delegates all it's responsibilities to it's extensions all of which are configurable; e.g. if you don't like the reports it generates, you can write your custom reporter in a few lines of code. |
13 | 18 |
|
14 |
| -==Quick start== |
15 |
| -Now that you have installed BDDfy, write your first test (this test is borrowed from ATM sample that you can install using nuget package BDDfy.Samples.ATM): |
| 19 | +##Quick start |
| 20 | +Now that you have installed BDDfy, write your first test (this test is borrowed from ATM sample that you can install using nuget package TestStack.BDDfy.Samples): |
16 | 21 |
|
17 |
| -{{{ |
18 |
| -[Story( |
| 22 | + [Story( |
19 | 23 | AsA = "As an Account Holder",
|
20 | 24 | IWant = "I want to withdraw cash from an ATM",
|
21 | 25 | SoThat = "So that I can get money when the bank is closed")]
|
22 |
| -public class AccountHasInsufficientFund |
23 |
| -{ |
24 |
| - private Card _card; |
25 |
| - private Atm _atm; |
26 |
| - |
27 |
| - // You can override step text using executable attributes |
28 |
| - [Given(StepText = "Given the account balance is $10")] |
29 |
| - void GivenAccountHasEnoughBalance() |
30 |
| - { |
31 |
| - _card = new Card(true, 10); |
32 |
| - } |
33 |
| - |
34 |
| - void AndGivenTheCardIsValid() |
35 |
| - { |
36 |
| - } |
37 |
| - |
38 |
| - void AndGivenTheMachineContainsEnoughMoney() |
39 |
| - { |
40 |
| - _atm = new Atm(100); |
41 |
| - } |
42 |
| - |
43 |
| - void WhenTheAccountHolderRequests20() |
44 |
| - { |
45 |
| - _atm.RequestMoney(_card, 20); |
46 |
| - } |
47 |
| - |
48 |
| - void ThenTheAtmShouldNotDispenseAnyMoney() |
49 |
| - { |
50 |
| - Assert.AreEqual(0, _atm.DispenseValue); |
51 |
| - } |
52 |
| - |
53 |
| - void AndTheAtmShouldSayThereAreInsufficientFunds() |
54 |
| - { |
55 |
| - Assert.AreEqual(DisplayMessage.InsufficientFunds, _atm.Message); |
56 |
| - } |
57 |
| - |
58 |
| - void AndTheCardShouldBeReturned() |
59 |
| - { |
60 |
| - Assert.IsFalse(_atm.CardIsRetained); |
61 |
| - } |
62 |
| - |
63 |
| - [Test] |
64 |
| - public void Execute() |
65 |
| - { |
66 |
| - this.BDDfy(); |
67 |
| - } |
68 |
| -} |
69 |
| - |
70 |
| -}}} |
| 26 | + public class AccountHasInsufficientFund |
| 27 | + { |
| 28 | + private Card _card; |
| 29 | + private Atm _atm; |
| 30 | + |
| 31 | + // You can override step text using executable attributes |
| 32 | + [Given(StepText = "Given the account balance is $10")] |
| 33 | + void GivenAccountHasEnoughBalance() |
| 34 | + { |
| 35 | + _card = new Card(true, 10); |
| 36 | + } |
| 37 | + |
| 38 | + void AndGivenTheCardIsValid() |
| 39 | + { |
| 40 | + } |
| 41 | + |
| 42 | + void AndGivenTheMachineContainsEnoughMoney() |
| 43 | + { |
| 44 | + _atm = new Atm(100); |
| 45 | + } |
| 46 | + |
| 47 | + void WhenTheAccountHolderRequests20() |
| 48 | + { |
| 49 | + _atm.RequestMoney(_card, 20); |
| 50 | + } |
| 51 | + |
| 52 | + void ThenTheAtmShouldNotDispenseAnyMoney() |
| 53 | + { |
| 54 | + Assert.AreEqual(0, _atm.DispenseValue); |
| 55 | + } |
| 56 | + |
| 57 | + void AndTheAtmShouldSayThereAreInsufficientFunds() |
| 58 | + { |
| 59 | + Assert.AreEqual(DisplayMessage.InsufficientFunds, _atm.Message); |
| 60 | + } |
| 61 | + |
| 62 | + void AndTheCardShouldBeReturned() |
| 63 | + { |
| 64 | + Assert.IsFalse(_atm.CardIsRetained); |
| 65 | + } |
| 66 | + |
| 67 | + [Test] |
| 68 | + public void Execute() |
| 69 | + { |
| 70 | + this.BDDfy(); |
| 71 | + } |
| 72 | + } |
| 73 | + |
71 | 74 |
|
72 | 75 | And this gives you a report like:
|
73 |
| -{{{ |
74 |
| -Story: Account holder withdraws cash |
75 |
| - As an Account Holder |
76 |
| - I want to withdraw cash from an ATM |
77 |
| - So that I can get money when the bank is closed |
78 |
| - |
79 |
| -Scenario: Account has insufficient fund |
80 |
| - Given the account balance is $10 |
81 |
| - And the card is valid |
82 |
| - When the account holder requests $20 |
83 |
| - Then the atm should not dispense any money |
84 |
| - And the atm should say there are insufficient funds |
85 |
| - And the card should be returned |
86 |
| -}}} |
| 76 | + |
| 77 | + Story: Account holder withdraws cash |
| 78 | + As an Account Holder |
| 79 | + I want to withdraw cash from an ATM |
| 80 | + So that I can get money when the bank is closed |
| 81 | + |
| 82 | + Scenario: Account has insufficient fund |
| 83 | + Given the account balance is $10 |
| 84 | + And the card is valid |
| 85 | + When the account holder requests $20 |
| 86 | + Then the atm should not dispense any money |
| 87 | + And the atm should say there are insufficient funds |
| 88 | + And the card should be returned |
87 | 89 |
|
88 | 90 | This is just the console report. Have a look at your output folder and you should see a nice html report too.
|
89 | 91 |
|
90 | 92 | If you want more control you can also use BDDfy's Fluent API. Here is another example done using the Fluent API:
|
91 | 93 |
|
92 |
| -{{{ |
93 |
| -[Test] |
94 |
| -public void CardHasBeenDisabled() |
95 |
| -{ |
96 |
| - this.Given(s => s.GivenTheCardIsDisabled()) |
97 |
| - .When(s => s.WhenTheAccountHolderRequests(20)) |
98 |
| - .Then(s => s.CardIsRetained(true), "Then the ATM should retain the card") |
99 |
| - .And(s => s.AndTheAtmShouldSayTheCardHasBeenRetained()) |
100 |
| - .BDDfy(htmlReportName: "ATM"); |
101 |
| -} |
102 |
| -}}} |
| 94 | + |
| 95 | + [Test] |
| 96 | + public void CardHasBeenDisabled() |
| 97 | + { |
| 98 | + this.Given(s => s.GivenTheCardIsDisabled()) |
| 99 | + .When(s => s.WhenTheAccountHolderRequests(20)) |
| 100 | + .Then(s => s.CardIsRetained(true), "Then the ATM should retain the card") |
| 101 | + .And(s => s.AndTheAtmShouldSayTheCardHasBeenRetained()) |
| 102 | + .BDDfy(htmlReportName: "ATM"); |
| 103 | + } |
103 | 104 |
|
104 | 105 | which gives you a report like:
|
105 |
| -{{{ |
106 |
| -Scenario: Card has been disabled |
107 |
| - Given the card is disabled |
108 |
| - When the account holder requests 20 |
109 |
| - Then the ATM should retain the card |
110 |
| - And the atm should say the card has been retained |
111 |
| -}}} |
112 |
| - |
113 |
| -==How does BDDfy work?== |
| 106 | + |
| 107 | + Scenario: Card has been disabled |
| 108 | + Given the card is disabled |
| 109 | + When the account holder requests 20 |
| 110 | + Then the ATM should retain the card |
| 111 | + And the atm should say the card has been retained |
| 112 | + |
| 113 | + |
| 114 | +##How does BDDfy work? |
114 | 115 | BDDfy uses quite a few conventions to make it frictionless to use. For your convenience, I will try to provide a quick tutorial below:
|
115 | 116 |
|
116 |
| -===Finding steps=== |
117 |
| -BDDfy scans your bddified classes for steps. Currently it has three ways of finding a step: |
118 |
| - * Using attributes |
119 |
| - * Using method name conventions |
120 |
| - * And using fluent API. |
| 117 | +###Finding steps |
| 118 | +BDDfy scans your BDDfied classes for steps. Currently it has three ways of finding a step: |
| 119 | + |
| 120 | +* Using attributes |
| 121 | +* Using method name conventions |
| 122 | +* And using fluent API. |
121 | 123 |
|
122 | 124 | BDDfy runs your steps in the following order: SetupState, ConsecutiveSetupState, Transition, ConsecutiveTransition, Assertion, ConsecutiveAssertion and TearDown. Some of these steps are reported in the console and html reports and some of them are not. Please read below for further information.
|
123 | 125 |
|
124 |
| -====Attributes==== |
| 126 | +###Attributes |
125 | 127 | BDDfy looks for a custom attribute called ExecutableAttribute on your method. To make it easier to use, ExecutableAttribute has a few subclasses that you can use: you may apply Given, AndGiven, When, AndWhen, Then, and AndThen attributes on any method you want to make available to BDDfy.
|
126 | 128 |
|
127 |
| -====Method name convention==== |
128 |
| -BDDfy uses some conventions to find methods that should be turned into steps. Here is the current conventions. The method name: |
| 129 | +###Method name convention |
| 130 | +BDDfy uses reflection to scan your classes for steps. In this mode, known as reflective mode, it has two ways of finding a step: using attributes and method name conventions. The following is the list of method name conventions: |
129 | 131 |
|
130 |
| - * ending with "Context" is considered as a setup method (not reported). |
131 |
| - * "Setup" is considered as as setup method (not reported). |
132 |
| - * starting with "Given" is considered as a setup method (reported). |
133 |
| - * starting with "AndGiven" is considered as a setup method that runs after Context, Setup and Given steps (reported). |
134 |
| - * starting with "When" is considered as a transition method (reported). |
135 |
| - * starting with "AndWhen" is considered as a transition method that runs after When steps (reported). |
136 |
| - * starting with "Then" is considered as an asserting method (reported). |
137 |
| - * starting with "And" is considered as an asserting method (reported). |
138 |
| - * starting with "TearDown" is considered as a finally method which is run after all the other steps (not reported). |
| 132 | +* Method name ending with `Context` is considered a setup method but doesn't get shown in the reports |
| 133 | +* Method name equaling `Setup` is a setup method but doesn't get showin in the reports |
| 134 | +* Method name starting with `Given` is a setup step that gets reported in the reports |
| 135 | +* Method name starting with `AndGiven` and 'And_given_' are considered setup steps running after 'Given' steps which is reported. |
| 136 | +* Method name starting with `When` is considered a state transition step and is reported |
| 137 | +* Method name starting with `AndWhen` and `And_when_` are considered state transition steps running after 'When' steps and is reported |
| 138 | +* Method name starting with `Then` is an asserting step and is reported |
| 139 | +* Method name starting with `And` and `AndThen` and `And_then_` are considered an asserting steps running after 'Then' step and is reported |
| 140 | +* Method name starting with `TearDown` is considered as tear down method that is always run at the end but doesn't get shown in the reports. |
139 | 141 |
|
140 | 142 | As you see in the above example you can mix and match the executable attributes and method name conventions to acheive great flexibility and power.
|
141 | 143 |
|
142 |
| -====Fluent API==== |
| 144 | +###Fluent API |
143 | 145 | Fluent API gives you the absolute power over step selection and their titles. When you use Fluent API for a test, the attributes and method name conventions are ignored for that test.
|
144 | 146 |
|
145 |
| -Pleasee note that you can have some tests using fluent API and some using a combination of attributes and method name conventions. Each .BDDfy() test works in isolation of others. |
146 |
| - |
147 |
| -==Other conventions== |
148 |
| -BDDfy prefers convention over configuration; but it also allows you to configure pretty much all the conventions. Here I will try to list some of the conventions and the way you can override them: |
149 |
| - |
150 |
| -===Method name convention=== |
151 |
| -As mentioned above, by default BDDfy uses method name convention to find your steps. You can override this using ExecutableAttribute or Fluent API as discussed above. |
152 |
| - |
153 |
| -===Step title convention=== |
154 |
| -BDDfy extracts steps titles differently depending on the way steps are found: |
155 |
| - |
156 |
| -====When using method name convention==== |
157 |
| - |
158 |
| -====When using ExecutableAttributes==== |
159 |
| - |
160 |
| -====When using Fluent API==== |
| 147 | +Please note that you can have some tests using fluent API and some using a combination of attributes and method name conventions. Each .BDDfy() test works in isolation of others. |
161 | 148 |
|
162 |
| -===Scenario title convention=== |
| 149 | +###Other conventions |
| 150 | +BDDfy prefers convention over configuration; but it also allows you to configure pretty much all the conventions. |
0 commit comments