File: README.rst

package info (click to toggle)
freshen 0.2-2
  • links: PTS, VCS
  • area: main
  • in suites: buster, jessie, jessie-kfreebsd, sid, stretch, wheezy
  • size: 524 kB
  • ctags: 332
  • sloc: python: 1,190; makefile: 2
file content (407 lines) | stat: -rwxr-xr-x 15,970 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
Freshen
=======

- Freshen is an acceptance testing framework for Python.
- It is built as a plugin for Nose_.
- It uses the (mostly) same syntax as Cucumber_.

What's New in Version 0.2?
--------------------------

- Freshen now supports Backgrounds_ for a feature.
- Freshen now supports the **But** keyword for steps of Scenarios_.
- Freshen now supports the simple case of `Step Argument Transforms`_.
- The parser now supports several natural language aliases for a keyword.
- If a natural language translation is not found for a keyword, English will be used.
- "@After" hooks are now run in the *opposite* order of which they are registered.
- Improved error handling and reporting.

There are also some modifications that are incompatible with Cucumber. 

- Only the step definition module named "steps" is used by default.
- Users can override this behavior with the "Use step definitions from" keyword.
- Freshen distinguishes "Given" steps from "When" steps and "Then" steps.

----------------------------------------------------------------------

Freshen Documentation
=====================

Most of the information shown here can also be found on the `Cucumber wiki`_, but here it is anyway:

Freshen tests are composed of two parts: `feature outlines`_ and `step definitions`_.


Feature outlines
----------------

Feature outlines are text files with a ``.feature`` extension. The purpose of this file is to
describe a feature in plain text understandable by a non-technical person such as a product manager
or user. However, these files are specially formatted and are parsed by Freshen in order to execute
real tests.

You can put your feature files anywhere you want in the source tree of your project, but it is
recommended to place them in a dedicated "features" directory.

A feature file contains (in this order):

- the step definition modules to use (*optional*, see `specifying step definition modules`_);
- the feature name with a free-form text description;
- a background (*optional*, see `backgrounds`_);
- one or more `scenarios`_ or `scenario outlines`_.


Scenarios
---------

A scenario is an example of an interaction a user would have as part of the feature. It is comprised
of a series of *steps*. Each step has to start with a keyword: **Given**, **When**, **Then**, **But** or **And**.
Here's an example for a calculator application (this example is included in the `source code`_)::

    Scenario: Divide regular numbers
      Given I have entered 3 into the calculator
      And I have entered 2 into the calculator
      When I press divide
      Then the result should be 1.5 on the screen


Scenario Outlines
-----------------

Sometimes it is useful to parametrize a scenario and run it multiple times, substituting values. For
this purpose, use *scenario outlines*. The format is the same as a scenario, except you can indicate
places where a value should be substituted using angle brackets: < and >. You specify the values
to be substituted using an "Examples" section that follows the scenario outline::

    Scenario Outline: Add two numbers
      Given I have entered <input_1> into the calculator
      And I have entered <input_2> into the calculator
      When I press <button>
      Then the result should be <output> on the screen

    Examples:
      | input_1 | input_2 | button | output |
      | 20      | 30      | add    | 50     |
      | 2       | 5       | add    | 7      |
      | 0       | 40      | add    | 40     |

In this case, the scenario will be executed once for each row in the table (except the first row,
which indicates which variable to substitute for).


Backgrounds
-----------

A feature may contain a background. It allows you to *add some context to the scenarios* 
in the current feature. A Background is much like a scenario containing a number of steps. 
The difference is when it is run. 
*The background is run before each of your scenarios but after any of your "@Before" hooks.*

Here is an example::

    Feature: Befriending
      In order to have some friends
      As a Facebook user
      I want to be able to manage my list of friends
      
      Background:
        Given I am the user Ken
        And I have friends Barbie, Cloe
    
      Scenario: Adding a new friend
        When I add a new friend named Jade
        Then I should have friends Barbie, Cloe, Jade
    
      Scenario: Removing a friend
        When I remove my friend Cloe
        Then I should have friends Barbie

*Note that background should be added in a feature only if it has a value for the client.* 
Otherwise, you can use tagged hooks (see Tags_ and Hooks_).


Step Definitions
----------------

When presented with a feature file, Freshen will execute each scenario. This involves iterating
over each step in turn and executing its *step definition*. Step definitions are python functions
adorned with a special decorator. Freshen knows which step definition function to execute by
matching the step's text against a regular expression associated with the definition. Here's an
example of a step definition file, which hopefully illustrates this point::

    from freshen import *

    import calculator
    
    @Before
    def before(sc):
        scc.calc = calculator.Calculator()
        scc.result = None
    
    @Given("I have entered (\d+) into the calculator")
    def enter(num):
        scc.calc.push(int(num))

    @When("I press (\w+)")
    def press(button):
        op = getattr(scc.calc, button)
        scc.result = op()

    @Then("the result should be (.*) on the screen")
    def check_result(value):
        assert_equal(str(scc.result), value)

In this example, you see a few step definitions, as well as a hook. Any captures (bits inside the 
parentheses) from the regular expression are passed to the step definition function as arguments.


Specifying Step Definition Modules
-----------------------------------

Step definitions are defined in python modules. By default, Freshen will try to load
a module named "steps" from the same directory as the ``.feature`` file. If that is not the
desired behavior, you can also explicitly specify which step definition modules to use
for a feature. To do this, use the keyword ``Using step definitions from``
(or its abbreviation: ``Using steps``) and specify which step definition modules you
want to use. Each module name must be a quoted string and must be relative to the
location of the feature file. You can specify one or more module names (they must be
separated by commas).

Here is an example::

    Using step definitions from: 'steps', 'step/page_steps'

    Feature: Destroy a document
      In order to take out one's anger on a document
      As an unsatisfied reader 
      I want to be able to rip off the pages of the document
    
      Scenario: Rip off a page
        Given a document of 5 pages
        And the page is 3
        When I rip off the current page
        Then the page is 3
        But the document has 4 pages

Although you have the opportunity to explicitly specify the step definition modules to use in Freshen, 
this is not a reason to fall into the `Feature-Coupled Steps Antipattern`_!

A step definition module can import other step definition modules. When doing this,
the actual step definition functions must be at the top level. For example::

    from other_step_module import *

A step definition module can be a python package, as long as all the relevant functions are imported
into ``__init__.py``.

The python path will automatically include the current working directory and the
directory of the ``.feature`` file.


Hooks
-----

It is often useful to do some work before each step or each scenario is executed. For this purpose,
you can make use of *hooks*. Identify them for Freshen by adorning them with "@Before", "@After"
(run before or after each scenario), or "@AfterStep" which is run after each step.


Context storage
---------------

Since the execution of each scenario is broken up between multiple step functions, it is often
necessary to share information between steps. It is possible to do this using global variables in
the step definition modules but, if you dislike that approach, Freshen provides three global
storage areas which can be imported from the `freshen` module. They are:

- ``glc``: Global context, never cleared - same as using a global variable
- ``ftc``: Feature context, cleared at the start of each feature
- ``scc``: Scenario context, cleared at the start of each scenario

These objects are built to mimic a JavaScript/Lua-like table, where fields can be accessed with
either the square bracket notation, or the attribute notation. They do not complain when a key
is missing::

    glc.stuff == gcc['stuff']  => True
    glc.doesnotexist           => None

Running steps from within step definitions
------------------------------------------

You can call out to a step definition from within another step using the same notation used in 
feature files. To do this, use the ``run_steps`` function::

    @Given('I do thing A')
    def do_a():
        #Do something useful.
        pass
    
    @Given('I have B')
    def having_b():
        #Do something useful.
        pass

    @Given('I do something that use both')
    def use_both():
        run_steps("""
                  Given I do thing A
                  And I have B
                  """)


Multi-line arguments
--------------------

Steps can have two types of multi-line arguments: multi-line strings and tables. Multi-line strings
look like Python docstrings, starting and terminating with three double quotes: ``"""``.

Tables look like the ones in the example section in scenario outlines. They are comprised of a
header and one or more rows. Fields are delimited using a pipe: ``|``.

Both tables and multi-line strings should be placed on the line following the step.

They will be passed to the step definition as the first argument. Strings are presented as regular
Python strings, whereas tables come across as a ``Table`` object. To get the rows, call
``table.iterrows()``.


Tags
----

A feature or scenario can be adorned with one or more tags. This helps classify features and
scenarios to the reader. Freshen makes use of tags in two ways. The first is by allowing selective
execution via the command line - this is described below. The second is by allowing `hooks`_ to be
executed selectively. A partial example::
    
    >> feature:
    
    @needs_tmp_file
    Scenario: A scenario that needs a temporary file
        Given ...
        When ...
    
    >> step definition:
    
    @Before("@needs_tmp_file")
    def needs_tmp_file(sc):
        make_tmp_file()


Step Argument Transforms
------------------------

Step definitions are specified as regular expressions. Freshen will pass any
captured sub-expressions (i.e. the parts in parentheses) to the step definition
function as a string. However, it is often necessary to convert those strings
into another type of object. For example, in the step::

    Then user bob shold be friends with user adelaide

we may need to convert "user bob" to the the object User(name='bob') and
"user adelaide" to User(name="adelaide"). To do this repeatedly would break
the "Do Not Repeat Yourself (DRY)" principle of good software development. Step
Argument Transforms allow you to specify an automatic transformation for 
arguments if they match a certain regular expression. These transforms are
created in the step defitnion file. For example::

    @Transform(r"^user (\w+)$")
    def transform_user(name):
        return User.objects.find(name)

    @Then(r"^(user \w+) should be friends with (user \w+)")
    def check_friends(user1, user2):
        # Here the arguments will already be User objects
        assert user1.is_friends_with(user2)

The two arguments to the "Then" step will be matched in the transform above
and converted into a User object before being passed to the step definition.

Ignoring directories
--------------------

If a directory contains files with the extension ``.feature`` but you'd like Freshen to skip over
it, simply place a file with the name ".freshenignore" in that directory.


Using with Django
-----------------

Django_ is a popular framework for web applications. Freshen can work in conjunction with the
`django-sane-testing`_ library to initialize the Django environment and databases before running
tests. This feature is enabled by using the ``--with-django`` option from django-sane-testing. You
can also use ``--with-djangoliveserver`` or ``--with-cherrypyliveserver`` to start a web server
before the tests run for use with a UI testing tool such as `Selenium`_.


Using with Selenium
-------------------

Selenium is not supported until plugin support is implemented. If you need to use Selenium, try
version 0.1.


Running
-------

Freshen runs as part of the nose framework, so all options are part of the ``nosetests`` command-
line tool.

Some useful flags for ``nosetests``:

- ``--with-freshen``: Enables Freshen
- ``-v``: Verbose mode will display each feature and scenario name as they are executed
- ``--tags``: Only run the features and scenarios with the given tags. Tags should follow this
  option as a comma-separated list. A tag may be prefixed with a tilde (``~``) to negate it and only
  execute features and scenarios which do *not* have the given tag.
- ``--language``: Run the tests using the designated language. See the
  ``Internationalization`` section for more details

You should be able to use all the other Nose features, like coverage or profiling for "free". You
can also run all your unit, doctests, and Freshen tests in one go. Please consult the `Nose manual`_
for more details.

Internationalization
--------------------

Freshen now supports 30 languages, exactly the same as cucumber, since the
"language" file was borrowed from the cucumber project. As long as your
``.feature`` files respect the syntax, the person in charge of writing the 
acceptance tests may write it down in his/her mother tongue. The only exception is
the new keyword for `specifying step definition modules`_ since it is not available
in Cucumber_. For the moment, this keyword is available only in English, French,
and Portugese. If you use another language, you must use the english keyword for this
particular keyword (or translate it and add it to the ``languages.yml`` file).

The 'examples' directory contains a French sample. It's a simple translation of
the english 'calc'. If you want to check the example, go to the 'calc_fr' 
directory, and run::

    $ nosetests --with-freshen --language=fr

The default language is English.


Additional notes
----------------

**Why copy Cucumber?** - Because it works and lots of people use it. Life is short, so why spend it
on coming up with new syntax for something that already exists?

**Why use Nose?** - Because it works and lots of people use it and it already does many useful
things. Life is short, so why spend it re-implementing coverage, profiling, test discovery, and
command like processing again?

**Can I contribute?** - Yes, please! While the tool is currently a copy of Cucumber's syntax,
there's no law that says it has to be that forever. If you have any ideas or suggestions (or bugs!),
please feel free to let me know, or simply clone the repo and play around.

.. _`Source code`: http://github.com/rlisagor/freshen
.. _`Nose`: http://somethingaboutorange.com/mrl/projects/nose/0.11.1/
.. _`Nose manual`: http://somethingaboutorange.com/mrl/projects/nose/0.11.1/testing.html
.. _`Cucumber`: http://cukes.info
.. _`Cucumber wiki`: http://wiki.github.com/aslakhellesoy/cucumber/
.. _`Feature-Coupled Steps Antipattern`: http://wiki.github.com/aslakhellesoy/cucumber/feature-coupled-steps-antipattern
.. _`Selenium`: http://seleniumhq.org/
.. _`Django`: http://www.djangoproject.com/
.. _`django-sane-testing`: http://devel.almad.net/trac/django-sane-testing/