Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 238 lines (175 sloc) 10.038 kb
0cc99e5 @claudiob Three basic steps: a_model, a_page, a_text
authored
1 Behavior-driven development (BDD) consists of [five steps](http://cukes.info/):
2
3 1. Describe behavior in plain text
4 2. Write a step definition
5 3. Run and watch it fail
6 4. Write code to make the step pass
7 5. Run again and see the step pass
8
9 Neverfails is a proof of concept to reduce this list to **two steps**:
10
11 1. Describe behaviour in plain text
12 2. Run and watch it pass
13
14 With neverfails, step definitions do not simply check whether the existing code satifies the required behaviour or not. They also **write the code** to make them pass.
15
7192b25 @claudiob Updated README: added a link to Rails branch
authored
16 Neverfails involves an ambitious idea: code generation based on specifications.
17 This idea does not depend on a specific platform or programming language.
18 In principle, it could be implemented with any framework.
19 In this [master branch](https://github.com/claudiob/neverfails/tree/master), I test this idea using Django as a web framework and Python as the programming language.
20 In the [rails branch](https://github.com/claudiob/neverfails/tree/rails), I use instead Ruby on Rails.
21 [cowboycoded](https://github.com/cowboycoded/never_fails) is also investigating this approach using Ruby and Rails.
0cc99e5 @claudiob Three basic steps: a_model, a_page, a_text
authored
22
23 Behavior-driven development in Django
24 =====================================
25
26 Before approaching neverfails, it is important to understand how Behaviour-Driven Development (BDD) typically takes place within a Django project.
27
28 Step 1 (Describe behavior in plain text)
29 ----------------------------------------
30
31 Say we want to create a web store for a *grocery* store, with a distinct page for each product. The *apples* page, for instance, will list the types and quantities of apples currently in store, showing "No apples left" if there are none left in the market. This scenario can be described as:
32
50aa514 @claudiob Fixed README (using new GitHub Flavored Markdown with syntax highligh…
authored
33 ``` cucumber
34 Feature: Apples
35 Scenario: No apples left
36 Given there are no apples
37 When I browse the list of apples
38 Then I should see the text "No apples left"
39 ```
0cc99e5 @claudiob Three basic steps: a_model, a_page, a_text
authored
40
41 Having described behavior in plain text, we create a blank Django project and make use of [lettuce](https://github.com/gabrielfalcao/lettuce) to run the steps. Lettuce is a BDD tool for python, 100% inspired on [cucumber](https://github.com/aslakhellesoy/cucumber).
42
43 The following commands set up a new `grocery` Django project with a basic SQLite database, and a virtual environment with lettuce:
44
50aa514 @claudiob Fixed README (using new GitHub Flavored Markdown with syntax highligh…
authored
45 ``` bash
46 django-admin.py startproject grocery
47 cd grocery
48 echo -e "\nDATABASES = {'default': {'ENGINE': 'django.db.backends.sqlite3', 'NAME': 'grocery.db',}}" >> settings.py
49 virtualenv env
50 source env/bin/activate
51 pip install lettuce
52 echo -e "\nINSTALLED_APPS += ('lettuce.django', )" >> settings.py
53 python manage.py syncdb --noinput
54 mkdir features
55 echo -e "Feature: Apples\n\tScenario: No apples left\n\t\tGiven there are no apples\n\t\tWhen I browse the list of apples\n\t\tThen I should see the text \"No apples left\"" > features/apples.feature
56 ```
0cc99e5 @claudiob Three basic steps: a_model, a_page, a_text
authored
57 The following commands run the project in a local server instance. They should be executed in a separate shell, since the basic Django server cannot be daemonized:
58
50aa514 @claudiob Fixed README (using new GitHub Flavored Markdown with syntax highligh…
authored
59 ``` bash
60 cd grocery
61 source env/bin/activate
62 python manage.py runserver
63 ```
0cc99e5 @claudiob Three basic steps: a_model, a_page, a_text
authored
64
65 Step 2 (Write step definitions)
66 -------------------------------
67
68 To make Django aware of what the actions in the scenario actually mean, we can either write new step definitions, or import some library that translates common actions into Python commands. One such popular library for Web applications is [webrat](https://github.com/brynary/webrat), but is only available for Ruby. A very limited Python-equivalent is [radish](https://github.com/ff0000/radish).
69
70 For the sake of the `grocery` example, we define the three steps of the `No apples left` scenario as follows:
71
50aa514 @claudiob Fixed README (using new GitHub Flavored Markdown with syntax highligh…
authored
72 * *Given there are no apples*: this step passes if a model called 'Apple' exist and if there are no instances of this model in the database
73 * *When I browse the list of apples*: this step passes if a page exists listing apples and if I can open that page in a browser
74 * *Then I should see the text "No apples left"*: this step passes if I see the text "No apples left" in that page
0cc99e5 @claudiob Three basic steps: a_model, a_page, a_text
authored
75
01ba062 @claudiob Fixed README
authored
76 The file `fails.py` in this package contains these definition in Python and lettuce code.
0cc99e5 @claudiob Three basic steps: a_model, a_page, a_text
authored
77
78 Step 3 (Run and watch it fail)
79 ------------------------------
80
81 The following commands include this file in the project and run the steps again:
82
50aa514 @claudiob Fixed README (using new GitHub Flavored Markdown with syntax highligh…
authored
83 ``` bash
84 pip install neverfails
85 echo -e "from neverfails.terrain import *\nfrom neverfails import fails" > terrain.py
86 python manage.py harvest -S features/
87 ```
0cc99e5 @claudiob Three basic steps: a_model, a_page, a_text
authored
88
89 The result is the following, indicating that the *first* step has failed:
90
50aa514 @claudiob Fixed README (using new GitHub Flavored Markdown with syntax highligh…
authored
91 ```
92 AssertionError: No model found called apple
93 ```
0cc99e5 @claudiob Three basic steps: a_model, a_page, a_text
authored
94
df0ff22 @claudiob Fixed README (removed sub-steps of Step 4)
authored
95 Step 4 (Write code to make the three steps pass)
96 -------------------------------------------------
0cc99e5 @claudiob Three basic steps: a_model, a_page, a_text
authored
97
98 To make the first step pass, we need to create an application called apples, add it to the list of installed apps, create an Apple model and store it in the database:
99
50aa514 @claudiob Fixed README (using new GitHub Flavored Markdown with syntax highligh…
authored
100 ``` bash
101 python manage.py startapp apples
102 echo -e "INSTALLED_APPS += ('apples', )" >> settings.py
103 echo -e "class Apple(models.Model):\n\tpass" >> apples/models.py
104 python manage.py syncdb --noinput
105 python manage.py harvest -S features/
106 ```
0cc99e5 @claudiob Three basic steps: a_model, a_page, a_text
authored
107
108 The result is now the following, indicating that the *second* step has failed:
109
50aa514 @claudiob Fixed README (using new GitHub Flavored Markdown with syntax highligh…
authored
110 ```
111 AssertionError: No URL pattern found matching apples/
112 ```
0cc99e5 @claudiob Three basic steps: a_model, a_page, a_text
authored
113
114 To make the second step pass, we need to create a URL pattern matching "apples/" that points to a blank HTML page:
115
50aa514 @claudiob Fixed README (using new GitHub Flavored Markdown with syntax highligh…
authored
116 ``` bash
117 echo -e "from django.views.generic import TemplateView\nurlpatterns += patterns('',\n\t(r'^apples/', TemplateView.as_view(template_name='apples.html')),\n)\n" >> urls.py
118 mkdir apples/templates
119 touch apples/templates/apples.html
120 python manage.py harvest -S features/
121 ```
0cc99e5 @claudiob Three basic steps: a_model, a_page, a_text
authored
122
123 The result is now the following, indicating that the *third* step has failed:
124
50aa514 @claudiob Fixed README (using new GitHub Flavored Markdown with syntax highligh…
authored
125 ```
126 AssertionError: The text "No apples" left was not found in the current page
127 ```
0cc99e5 @claudiob Three basic steps: a_model, a_page, a_text
authored
128
df0ff22 @claudiob Fixed README (removed sub-steps of Step 4)
authored
129 To make the third step pass, we need to add the text "No apples left" to the page that lists apples:
50aa514 @claudiob Fixed README (using new GitHub Flavored Markdown with syntax highligh…
authored
130
131 ``` bash
132 echo -e "No apples left" >> apples/templates/apples.html
133 python manage.py harvest -S features/
134 ```
0cc99e5 @claudiob Three basic steps: a_model, a_page, a_text
authored
135
136 Step 5 (Run again and see the step pass)
137 ----------------------------------------
138
139 Finally, the three steps pass and running them again returns the message:
140
50aa514 @claudiob Fixed README (using new GitHub Flavored Markdown with syntax highligh…
authored
141 ``` cucumber
142 Feature: Apples
143 Scenario: No apples left
144 Given there are no apples
145 When I browse the list of apples
146 Then I should see the text "No apples left"
147
148 1 feature (1 passed)
149 1 scenario (1 passed)
150 3 steps (3 passed)
151 ```
0cc99e5 @claudiob Three basic steps: a_model, a_page, a_text
authored
152
153 Neverfails does all of this, so you don't have to
154 =================================================
155
df0ff22 @claudiob Fixed README (removed sub-steps of Step 4)
authored
156 The `grocery` example shows that Behaviour-Driven Development is time-consuming even for very small applications, with an empty model and a view showing one sentence.
157 Time is spent watching the tests fail and writing snippets of code that are common to every web application (creating a model, filling view with text and so on).
0cc99e5 @claudiob Three basic steps: a_model, a_page, a_text
authored
158
159 Neverfails reduces this time by automatically creating the missing snippets of code when a step fails.
160
161 Step 1 (Describe behavior in plain text)
162 ----------------------------------------
163
164 Continuing with the grocery example, say we want to add this new scenario:
165
50aa514 @claudiob Fixed README (using new GitHub Flavored Markdown with syntax highligh…
authored
166 ``` cucumber
167 Feature: Bananas
168 Scenario: No bananas left
169 Given there are no bananas
170 When I browse the list of bananas
171 Then I should see the text "No bananas left"
172 ```
0cc99e5 @claudiob Three basic steps: a_model, a_page, a_text
authored
173
174 The following commands add the previous scenario to the grocery project and include neverfails step definitions:
175
50aa514 @claudiob Fixed README (using new GitHub Flavored Markdown with syntax highligh…
authored
176 ``` bash
177 echo -e "Feature: Bananas\n\tScenario: No bananas left\n\t\tGiven there are no bananas\n\t\tWhen I browse the list of bananas\n\t\tThen I should see the text \"No bananas left\"" > features/bananas.feature
178 echo -e "from neverfails.terrain import *\nfrom neverfails import neverfails" >| terrain.py
179 ```
0cc99e5 @claudiob Three basic steps: a_model, a_page, a_text
authored
180
181 Step 2 (Run and watch it pass)
182 ------------------------------
183
184 Both the `apples` and the `bananas` scenario can be run with the command:
185
50aa514 @claudiob Fixed README (using new GitHub Flavored Markdown with syntax highligh…
authored
186 ``` bash
187 python manage.py harvest -S features/
188 ```
0cc99e5 @claudiob Three basic steps: a_model, a_page, a_text
authored
189
190 The `apples` scenario passes since we already wrote all its required. The `bananas` scenario, though, passes as well:
191
50aa514 @claudiob Fixed README (using new GitHub Flavored Markdown with syntax highligh…
authored
192 ``` cucumber
193 Feature: Apples
194 Scenario: No apples left
195 Given there are no apples
196 When I browse the list of apples
197 Then I should see the text "No apples left"
198
199 Feature: Bananas
200 Scenario: No bananas left
201 Given there are no bananas
202 Creating tables ...
203 Creating table bananas_banana
204 Installing custom SQL ...
205 Installing indexes ...
206 When I browse the list of bananas
207 Then I should see the text "No bananas left"
208
209 2 features (2 passed)
210 2 scenarios (2 passed)
211 6 steps (6 passed)
212 ```
0cc99e5 @claudiob Three basic steps: a_model, a_page, a_text
authored
213
214 How neverfails works
215 ====================
216
217 Similarly to lettuce, neverfails recognizes the steps using regular expressions and checks whether they pass or fail. If the step fails, neverfails *does not* raise an AssertionError but runs the code to make the step pass, then runs the step again.
218
219 So far, neverfails is only able to recognize the three kinds of step included in the `grocery` sample project: creating a model, creating a view, adding text to that view. This is why I call neverfails a proof of concept. If other people find this project interesting (or if I get more time to work on this), then neverfails will grow up to the point where people with no programming experience will be able to create complex web applications by describing what they wish for.
0ebc461 @claudiob Fixed README (added requirements to run the sample code and link to P…
authored
220
221 Installing neverfails
222 =====================
223
224 To follow the example described above, you need [pip](http://pypi.python.org/pypi/pip), [virtualenv](http://www.virtualenv.org) and [django](http://www.djangoproject.com/) installed on your machine.
225 The following commands will install these three packages, given you already have Python installed with [setuptools](http://pypi.python.org/pypi/setuptools) enabled:
226
50aa514 @claudiob Fixed README (using new GitHub Flavored Markdown with syntax highligh…
authored
227 ``` bash
228 easy_install pip
229 pip install django
230 pip install virtualenv
231 ```
0ebc461 @claudiob Fixed README (added requirements to run the sample code and link to P…
authored
232
233 The actual neverfails package can either be [downloaded from GitHub](https://github.com/claudiob/neverfails) or [installed from PyPi](http://pypi.python.org/pypi/neverfails) by running:
234
50aa514 @claudiob Fixed README (using new GitHub Flavored Markdown with syntax highligh…
authored
235 ``` bash
236 pip install neverfails
237 ```
Something went wrong with that request. Please try again.