forked from test_framework/pytest-bdd
Fixed the docs and few spelling error.
This commit is contained in:
parent
bf8e99f94a
commit
04a278eb97
|
@ -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
|
||||
|
|
41
README.rst
41
README.rst
|
@ -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 can’t 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 can’t 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 it’s 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 don’t use Scenario Outline, but use just Scenario,
|
||||
just because it’s 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
|
||||
=======
|
||||
|
|
|
@ -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'),
|
||||
]
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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')
|
||||
|
|
Loading…
Reference in New Issue