Fixed the docs and few spelling error.

This commit is contained in:
Oleg Pidsadnyi 2013-08-18 02:02:50 +02:00
parent bf8e99f94a
commit 04a278eb97
8 changed files with 83 additions and 57 deletions

View File

@ -1,7 +1,4 @@
language: python
before_install:
- "export DISPLAY=:99.0"
- "sh -e /etc/init.d/xvfb start"
# command to install dependencies
install:
- pip install python-coveralls

View File

@ -8,6 +8,18 @@ BDD library for the py.test runner
.. |Coverrals| image:: https://coveralls.io/repos/olegpidsadnyi/pytest-bdd/badge.png?branch=master
:target: https://coveralls.io/r/olegpidsadnyi/pytest-bdd
pytest-bdd implements a subset of Gherkin language for the automation of the project
requirements testing and easier behavioral driven development.
Unlike many other BDD tools it doesn't require a separate runner and benefits from
the power and flexibility of the pytest. It allows to unify your unit and functional
tests, easier continuous integration server configuration and maximal reuse of the
tests setup.
Pytest fixtures written for the unit tests can be reused for the setup and actions
mentioned in the feature steps with dependency injection, which allows a true BDD
just-enough specification of the requirements without maintaining any context object
containing the side effects of the Gherkin imperative declarations.
Install pytest-bdd
==================
@ -83,8 +95,8 @@ function with multiple step names simply decorate it multiple times:
Note that the given step aliases are independent and will be executed
when mentioned.
For example if you assoicate your resource to some owner or not. Admin
user cant be an author of the article, but article should have some
For example if you associate your resource to some owner or not. Admin
user cant be an author of the article, but articles should have a
default author.
::
@ -101,11 +113,9 @@ default author.
Step parameters
===============
Sometimes it is hard to write good scenarios without duplicating most of
contents of existing scenario. For example if you create some object
with static param value, you might want to create another test with
different param value. By Gherkin specification its possible to have
parameters in steps: http://docs.behat.org/guides/1.gherkin.html
Scenarios can be parametrized to cover few cases. In Gherkin the variable
templates are written using corner braces as <somevalue>.
Example:
::
@ -115,14 +125,17 @@ Example:
When I eat <eat> cucumbers
Then I should have <left> cucumbers
As you can see we dont use Scenario Outline, but use just Scenario,
just because its simple to implement for pytest.
Unlike other tools, pytest-bdd implements the scenario outline not in the
feature files, but in the python code using pytest parametrization.
The reason for this is that it is very often that some simple pythonic type
is needed in the parameters like a datetime or a dictionary, which makes it
more difficult to express in the text files and preserve the correct format.
The code will look like:
::
# here we use pytest power to parametrize test
# Here we use pytest to parametrize the test with the parameters table
@pytest.mark.parametrize(
['start', 'eat', 'left'],
[(12, 5, 7)])
@ -130,10 +143,10 @@ The code will look like:
'parametrized.feature',
'Parametrized given, when, thens',
)
# note that we should receive same arguments in function that we use for test parametrization either directly
# or indirectly (throught fixtures)
# Note that we should take the same arguments in the test function that we use
# for the test parametrization either directly or indirectly (fixtures depend on them).
def test_parametrized(start, eat, left):
"""We don't need to do anything here, everything will be managed by scenario decorator."""
"""We don't need to do anything here, everything will be managed by the scenario decorator."""
@given('there are <start> cucumbers')
@ -233,7 +246,7 @@ List of known subplugins:
::
* pytest-bdd-splinter -- collection of fixtures for real browser BDD testing
* pytest-bdd-splinter -- collection of fixtures for the real browser BDD testing
License
=======

View File

@ -41,7 +41,7 @@ master_doc = 'index'
# General information about the project.
project = u'Pytest-BDD'
copyright = u'2013, Oleg Pidsadnyi, Anatoly Bubenkov'
copyright = u'2013, Oleg Pidsadnyi'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
@ -184,7 +184,7 @@ latex_elements = {
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
('index', 'Pytest-BDD.tex', u'Pytest-BDD Documentation',
u'Oleg Pidsadnyi, Anatoly Bubenkov', 'manual'),
u'Oleg Pidsadnyi', 'manual'),
]
# The name of an image file (relative to this directory) to place at the top of
@ -214,7 +214,7 @@ latex_documents = [
# (source start file, name, description, authors, manual section).
man_pages = [
('index', 'pytest-bdd', u'Pytest-BDD Documentation',
[u'Oleg Pidsadnyi, Anatoly Bubenkov'], 1)
[u'Oleg Pidsadnyi'], 1)
]
# If true, show URL addresses after external links.
@ -228,7 +228,7 @@ man_pages = [
# dir menu entry, description, category)
texinfo_documents = [
('index', 'Pytest-BDD', u'Pytest-BDD Documentation',
u'Oleg Pidsadnyi, Anatoly Bubenkov', 'Pytest-BDD', 'One line description of project.',
u'Oleg Pidsadnyi', 'Pytest-BDD', 'One line description of project.',
'Miscellaneous'),
]

View File

@ -1,24 +1,6 @@
.. Pytest-BDD documentation master file, created by
sphinx-quickstart on Sun Apr 7 21:07:56 2013.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
Welcome to Pytest-BDD's documentation!
======================================
Contents:
.. toctree::
:maxdepth: 2
.. automodule:: pytest_bdd
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`
.. contents::
.. include:: ../README.rst

View File

@ -1,9 +1,12 @@
"""Feature.
The way of describing the behavior is based on Gherkin language, but a very
limited version. It doesn't support any parameter tables or any variables.
limited version. It doesn't support any parameter tables.
If the parametrization is needed to generate more test cases it can be done
on the fixture level of the pytest.
The <variable> syntax can be used here to make a connection between steps and
it will also validate the parameters mentioned in the steps with ones
provided in the pytest parametrization table.
Syntax example:
@ -18,6 +21,7 @@ Syntax example:
:note: The "#" symbol is used for comments.
:note: There're no multiline steps, the description of the step must fit in
one line.
"""
import re # pragma: no cover
@ -82,7 +86,9 @@ def remove_prefix(line):
"""Remove the step prefix (Scenario, Given, When, Then or And).
:param line: Line of the Feature file.
:return: Line without the prefix.
"""
for prefix in STEP_PREFIXES:
if line.startswith(prefix):
@ -97,6 +103,7 @@ class Feature(object):
"""Parse the feature file.
:param filename: Relative path to the feature file.
"""
self.scenarios = {}
@ -134,6 +141,17 @@ class Feature(object):
@classmethod
def get_feature(cls, filename):
"""Get a feature by the filename.
:param filename: Filename of the feature file.
:return: `Feature` instance from the parsed feature cache.
:note: The features are parsed on the execution of the test and
stored in the global variable cache to improve the performance
when multiple scenarios are referencing the same file.
"""
feature = features.get(filename)
if not feature:
feature = Feature(filename)

View File

@ -28,7 +28,7 @@ class NotEnoughScenarioParams(Exception): # pragma: no cover
def scenario(feature_name, scenario_name):
"""Scenario. May be called both as decorator and as just normal function"""
"""Scenario. May be called both as decorator and as just normal function."""
def decorator(request):
@ -43,14 +43,14 @@ def scenario(feature_name, scenario_name):
scenario = feature.scenarios[scenario_name]
except KeyError:
raise ScenarioNotFound(
'Scenario "{0}" in feature "{1}" is not found'.format(scenario_name, feature_name))
'Scenario "{0}" in feature "{1}" is not found.'.format(scenario_name, feature_name))
resolved_params = scenario.params.intersection(request.fixturenames)
if scenario.params != resolved_params:
raise NotEnoughScenarioParams(
"""Scenario "{0}" in feature "{1}" was not able to resolve all parameters declared.
Should resolve params: {2}, but resolved only: {3}""".format(
"""Scenario "{0}" in the feature "{1}" was not able to resolve all declared parameters."""
"""Should resolve params: {2}, but resolved only: {3}.""".format(
scenario_name, feature_name, sorted(scenario.params), sorted(resolved_params)))
# Execute scenario's steps
@ -62,10 +62,10 @@ Should resolve params: {2}, but resolved only: {3}""".format(
_scenario.pytestbdd_params = set()
if isinstance(request, python.FixtureRequest):
# we called as a normal function
# Called as a normal function.
return _scenario(request)
# we called as a decorator, so modify the returned function to add parameters from a decorated function
# Used as a decorator. Modify the returned function to add parameters from a decorated function.
func_args = inspect.getargspec(request).args
if 'request' in func_args:
func_args.remove('request')

View File

@ -56,6 +56,7 @@ def given(name, fixture=None):
:raises: StepError in case of wrong configuration.
:note: Can't be used as a decorator when the fixture is specified.
"""
name = remove_prefix(name)
@ -72,7 +73,9 @@ def when(name):
"""When step decorator.
:param name: Step name.
:raises: StepError in case of wrong configuration.
"""
return _step_decorator(WHEN, name)
@ -81,7 +84,9 @@ def then(name):
"""Then step decorator.
:param name: Step name.
:raises: StepError in case of wrong configuration.
"""
return _step_decorator(THEN, name)
@ -90,13 +95,16 @@ def _not_a_fixture_decorator(func):
"""Function that prevents the decoration.
:param func: Function that is going to be decorated.
:raises: `StepError` if was used as a decorator.
"""
raise StepError('Cannot be used as a decorator when the fixture is specified')
def _step_decorator(step_type, step_name):
"""Step decorator for the type and the name.
:param step_type: Step type (GIVEN, WHEN or THEN).
:param step_name: Step name as in the feature file.
@ -104,6 +112,7 @@ def _step_decorator(step_type, step_name):
:note: If the step type is GIVEN it will automatically apply the pytest
fixture decorator to the step function.
"""
step_name = remove_prefix(step_name)
@ -113,7 +122,7 @@ def _step_decorator(step_type, step_name):
if step_type == GIVEN:
if not hasattr(func, '_pytestfixturefunction'):
# avoid overfixturing of a fixture
# Avoid multiple wrapping a fixture
func = pytest.fixture(func)
step_func = lambda request: request.getfuncargvalue(func.__name__)
step_func.__doc__ = func.__doc__
@ -124,7 +133,7 @@ def _step_decorator(step_type, step_name):
def lazy_step_func():
return step_func
# preserve docstring
# Preserve a docstring
lazy_step_func.__doc__ = func.__doc__
contribute_to_module(
@ -139,9 +148,14 @@ def _step_decorator(step_type, step_name):
def recreate_function(func, module=None, name=None, add_args=()):
"""Recreate a function, replacing some info.
:param func: Function object.
:param module: Module to contribute to.
:param add_args: Additional arguments to add to function."""
:param add_args: Additional arguments to add to function.
:return: Function copy.
"""
def get_code(func):
return func.__code__ if PY3 else func.func_code
@ -186,6 +200,7 @@ def contribute_to_module(module, name, func):
:param module: Module to contribute to.
:param name: Attribute name.
:param func: Function object.
"""
func = recreate_function(func, module=module)

View File

@ -22,16 +22,17 @@ def test_parametrized_wrongly(request):
'parametrized.feature',
'Parametrized given, when, thens',
)
def test_parametrized_wrongly(request):
def wrongly_parametrized(request):
pass
with pytest.raises(NotEnoughScenarioParams) as exc:
test_parametrized_wrongly(request)
wrongly_parametrized(request)
assert exc.value.args == (
'Scenario "Parametrized given, when, thens" in feature "parametrized.feature" was not able to resolve all '
'parameters declared.\nShould resolve params: [\'eat\', \'left\', \'start\'], but resolved only: []',
)
assert exc.value.args == (
"""Scenario "Parametrized given, when, thens" in the feature "parametrized.feature" was not able to """
"""resolve all declared parameters. """
"""Should resolve params: [\'eat\', \'left\', \'start\'], but resolved only: []."""
)
@given('there are <start> cucumbers')