- fix doc references, refactor fixtures docs to more quickly start
with examples instead of big text blobgs - also silence -q and -qq reporting some more
This commit is contained in:
parent
cda84fb566
commit
30b10a6950
|
@ -21,22 +21,23 @@ class FixtureFunctionMarker:
|
||||||
return function
|
return function
|
||||||
|
|
||||||
|
|
||||||
# XXX a test fails when scope="function" how it should be, investigate
|
def fixture(scope="function", params=None, autoactive=False):
|
||||||
def fixture(scope=None, params=None, autoactive=False):
|
""" (return a) decorator to mark a fixture factory function.
|
||||||
""" return a decorator to mark a fixture factory function.
|
|
||||||
|
|
||||||
The name of the fixture function can be referenced in a test context
|
This decorator can be used (directly or with parameters) to define
|
||||||
to cause its invocation ahead of running tests. Test modules or classes
|
a fixture function. The name of the fixture function can later be
|
||||||
can use the pytest.mark.usefixtures(fixturename) marker to specify
|
referenced to cause its invocation ahead of running tests: test
|
||||||
needed fixtures. Test functions can also use fixture names as input
|
modules or classes can use the pytest.mark.usefixtures(fixturename)
|
||||||
|
marker and test functions can directly use fixture names as input
|
||||||
arguments in which case the fixture instance returned from the fixture
|
arguments in which case the fixture instance returned from the fixture
|
||||||
function will be injected.
|
function will be injected.
|
||||||
|
|
||||||
:arg scope: the scope for which this fixture is shared, one of
|
:arg scope: the scope for which this fixture is shared, one of
|
||||||
"function", "class", "module", "session". Defaults to "function".
|
"function" (default), "class", "module", "session".
|
||||||
|
|
||||||
:arg params: an optional list of parameters which will cause multiple
|
:arg params: an optional list of parameters which will cause multiple
|
||||||
invocations of the fixture functions and their dependent
|
invocations of the fixture function and all of the tests
|
||||||
tests.
|
using it.
|
||||||
|
|
||||||
:arg autoactive: if True, the fixture func is activated for all tests that
|
:arg autoactive: if True, the fixture func is activated for all tests that
|
||||||
can see it. If False (the default) then an explicit
|
can see it. If False (the default) then an explicit
|
||||||
|
@ -992,7 +993,7 @@ def scopeproperty(name=None, doc=None):
|
||||||
|
|
||||||
|
|
||||||
class FixtureRequest(FuncargnamesCompatAttr):
|
class FixtureRequest(FuncargnamesCompatAttr):
|
||||||
""" A request for fixtures from a test or setup function.
|
""" A request for fixtures from a test or fixture function.
|
||||||
|
|
||||||
A request object gives access to attributes of the requesting
|
A request object gives access to attributes of the requesting
|
||||||
test context. It has an optional ``param`` attribute in case
|
test context. It has an optional ``param`` attribute in case
|
||||||
|
@ -1019,7 +1020,7 @@ class FixtureRequest(FuncargnamesCompatAttr):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def node(self):
|
def node(self):
|
||||||
""" underlying collection node (depends on request scope)"""
|
""" underlying collection node (depends on current request scope)"""
|
||||||
return self._getscopeitem(self.scope)
|
return self._getscopeitem(self.scope)
|
||||||
|
|
||||||
def _getfixturedeflist(self, argname):
|
def _getfixturedeflist(self, argname):
|
||||||
|
@ -1227,12 +1228,13 @@ class FixtureRequest(FuncargnamesCompatAttr):
|
||||||
mp.setattr(self, "scope", scope)
|
mp.setattr(self, "scope", scope)
|
||||||
|
|
||||||
# prepare finalization according to scope
|
# prepare finalization according to scope
|
||||||
|
# (XXX analyse exact finalizing mechanics / cleanup)
|
||||||
self.session._setupstate.addfinalizer(fixturedef.finish, self.node)
|
self.session._setupstate.addfinalizer(fixturedef.finish, self.node)
|
||||||
self._fixturemanager.addargfinalizer(fixturedef.finish, argname)
|
self._fixturemanager.addargfinalizer(fixturedef.finish, argname)
|
||||||
for subargname in fixturedef.fixturenames: # XXX all deps?
|
for subargname in fixturedef.fixturenames: # XXX all deps?
|
||||||
self._fixturemanager.addargfinalizer(fixturedef.finish, subargname)
|
self._fixturemanager.addargfinalizer(fixturedef.finish, subargname)
|
||||||
mp.setattr(self, "addfinalizer", fixturedef.addfinalizer)
|
mp.setattr(self, "addfinalizer", fixturedef.addfinalizer)
|
||||||
# finally perform the factory call
|
# finally perform the fixture call
|
||||||
val = fixturedef.execute(request=self)
|
val = fixturedef.execute(request=self)
|
||||||
mp.undo()
|
mp.undo()
|
||||||
return val
|
return val
|
||||||
|
|
|
@ -209,7 +209,7 @@ class TerminalReporter:
|
||||||
self.currentfspath = -2
|
self.currentfspath = -2
|
||||||
|
|
||||||
def pytest_collection(self):
|
def pytest_collection(self):
|
||||||
if not self.hasmarkup:
|
if not self.hasmarkup and self.config.option.verbose >=1:
|
||||||
self.write("collecting ... ", bold=True)
|
self.write("collecting ... ", bold=True)
|
||||||
|
|
||||||
def pytest_collectreport(self, report):
|
def pytest_collectreport(self, report):
|
||||||
|
@ -224,6 +224,9 @@ class TerminalReporter:
|
||||||
self.report_collect()
|
self.report_collect()
|
||||||
|
|
||||||
def report_collect(self, final=False):
|
def report_collect(self, final=False):
|
||||||
|
if self.config.option.verbose < 0:
|
||||||
|
return
|
||||||
|
|
||||||
errors = len(self.stats.get('error', []))
|
errors = len(self.stats.get('error', []))
|
||||||
skipped = len(self.stats.get('skipped', []))
|
skipped = len(self.stats.get('skipped', []))
|
||||||
if final:
|
if final:
|
||||||
|
@ -455,8 +458,8 @@ class TerminalReporter:
|
||||||
msg = "%s in %.2f seconds" %(line, session_duration)
|
msg = "%s in %.2f seconds" %(line, session_duration)
|
||||||
if self.verbosity >= 0:
|
if self.verbosity >= 0:
|
||||||
self.write_sep("=", msg, bold=True)
|
self.write_sep("=", msg, bold=True)
|
||||||
else:
|
#else:
|
||||||
self.write_line(msg, bold=True)
|
# self.write_line(msg, bold=True)
|
||||||
|
|
||||||
def summary_deselected(self):
|
def summary_deselected(self):
|
||||||
if 'deselected' in self.stats:
|
if 'deselected' in self.stats:
|
||||||
|
|
|
@ -5,6 +5,7 @@ Release announcements
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:maxdepth: 2
|
:maxdepth: 2
|
||||||
|
|
||||||
|
release-2.3.0
|
||||||
release-2.2.4
|
release-2.2.4
|
||||||
release-2.2.2
|
release-2.2.2
|
||||||
release-2.2.1
|
release-2.2.1
|
||||||
|
|
|
@ -10,16 +10,15 @@ py.test reference documentation
|
||||||
builtin.txt
|
builtin.txt
|
||||||
customize.txt
|
customize.txt
|
||||||
assert.txt
|
assert.txt
|
||||||
funcargs.txt
|
fixture.txt
|
||||||
funcarg_compare.txt
|
parametrize.txt
|
||||||
setup.txt
|
|
||||||
xunit_setup.txt
|
xunit_setup.txt
|
||||||
capture.txt
|
capture.txt
|
||||||
monkeypatch.txt
|
monkeypatch.txt
|
||||||
xdist.txt
|
xdist.txt
|
||||||
tmpdir.txt
|
tmpdir.txt
|
||||||
skipping.txt
|
|
||||||
mark.txt
|
mark.txt
|
||||||
|
skipping.txt
|
||||||
recwarn.txt
|
recwarn.txt
|
||||||
unittest.txt
|
unittest.txt
|
||||||
nose.txt
|
nose.txt
|
||||||
|
|
|
@ -24,9 +24,8 @@ you will see the return value of the function call::
|
||||||
|
|
||||||
$ py.test test_assert1.py
|
$ py.test test_assert1.py
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev2
|
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19
|
||||||
plugins: xdist, bugzilla, cache, oejskit, pep8, cov
|
collected 1 items
|
||||||
collecting ... collected 1 items
|
|
||||||
|
|
||||||
test_assert1.py F
|
test_assert1.py F
|
||||||
|
|
||||||
|
@ -39,7 +38,7 @@ you will see the return value of the function call::
|
||||||
E + where 3 = f()
|
E + where 3 = f()
|
||||||
|
|
||||||
test_assert1.py:5: AssertionError
|
test_assert1.py:5: AssertionError
|
||||||
========================= 1 failed in 0.02 seconds =========================
|
========================= 1 failed in 0.01 seconds =========================
|
||||||
|
|
||||||
py.test has support for showing the values of the most common subexpressions
|
py.test has support for showing the values of the most common subexpressions
|
||||||
including calls, attributes, comparisons, and binary and unary
|
including calls, attributes, comparisons, and binary and unary
|
||||||
|
@ -107,9 +106,8 @@ if you run this module::
|
||||||
|
|
||||||
$ py.test test_assert2.py
|
$ py.test test_assert2.py
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev2
|
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19
|
||||||
plugins: xdist, bugzilla, cache, oejskit, pep8, cov
|
collected 1 items
|
||||||
collecting ... collected 1 items
|
|
||||||
|
|
||||||
test_assert2.py F
|
test_assert2.py F
|
||||||
|
|
||||||
|
@ -127,7 +125,7 @@ if you run this module::
|
||||||
E '5'
|
E '5'
|
||||||
|
|
||||||
test_assert2.py:5: AssertionError
|
test_assert2.py:5: AssertionError
|
||||||
========================= 1 failed in 0.02 seconds =========================
|
========================= 1 failed in 0.01 seconds =========================
|
||||||
|
|
||||||
Special comparisons are done for a number of cases:
|
Special comparisons are done for a number of cases:
|
||||||
|
|
||||||
|
@ -171,7 +169,6 @@ you can run the test module and get the custom output defined in
|
||||||
the conftest file::
|
the conftest file::
|
||||||
|
|
||||||
$ py.test -q test_foocompare.py
|
$ py.test -q test_foocompare.py
|
||||||
collecting ... collected 1 items
|
|
||||||
F
|
F
|
||||||
================================= FAILURES =================================
|
================================= FAILURES =================================
|
||||||
_______________________________ test_compare _______________________________
|
_______________________________ test_compare _______________________________
|
||||||
|
@ -184,7 +181,6 @@ the conftest file::
|
||||||
E vals: 1 != 2
|
E vals: 1 != 2
|
||||||
|
|
||||||
test_foocompare.py:8: AssertionError
|
test_foocompare.py:8: AssertionError
|
||||||
1 failed in 0.02 seconds
|
|
||||||
|
|
||||||
.. _assert-details:
|
.. _assert-details:
|
||||||
.. _`assert introspection`:
|
.. _`assert introspection`:
|
||||||
|
|
|
@ -17,7 +17,8 @@ to get an overview on the globally available helpers.
|
||||||
.. automodule:: pytest
|
.. automodule:: pytest
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
.. _builtinresources:
|
.. _builtinfixtures:
|
||||||
|
.. _builtinfuncargs:
|
||||||
|
|
||||||
Builtin resources / function arguments
|
Builtin resources / function arguments
|
||||||
-----------------------------------------------------
|
-----------------------------------------------------
|
||||||
|
@ -27,11 +28,8 @@ You can ask for available builtin or project-custom
|
||||||
|
|
||||||
$ py.test --fixtures
|
$ py.test --fixtures
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev2
|
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19
|
||||||
plugins: xdist, bugzilla, cache, oejskit, pep8, cov
|
|
||||||
collected 0 items
|
collected 0 items
|
||||||
pytestconfig
|
|
||||||
the pytest config object with access to command line opts.
|
|
||||||
capsys
|
capsys
|
||||||
enables capturing of writes to sys.stdout/sys.stderr and makes
|
enables capturing of writes to sys.stdout/sys.stderr and makes
|
||||||
captured output available via ``capsys.readouterr()`` method calls
|
captured output available via ``capsys.readouterr()`` method calls
|
||||||
|
@ -76,7 +74,5 @@ You can ask for available builtin or project-custom
|
||||||
See http://docs.python.org/library/warnings.html for information
|
See http://docs.python.org/library/warnings.html for information
|
||||||
on warning categories.
|
on warning categories.
|
||||||
|
|
||||||
cov
|
|
||||||
A pytest funcarg that provides access to the underlying coverage object.
|
|
||||||
|
|
||||||
============================= in 0.01 seconds =============================
|
============================= in 0.00 seconds =============================
|
||||||
|
|
|
@ -64,9 +64,8 @@ of the failing function and hide the other one::
|
||||||
|
|
||||||
$ py.test
|
$ py.test
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev2
|
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19
|
||||||
plugins: xdist, bugzilla, cache, oejskit, pep8, cov
|
collected 2 items
|
||||||
collecting ... collected 2 items
|
|
||||||
|
|
||||||
test_module.py .F
|
test_module.py .F
|
||||||
|
|
||||||
|
@ -79,8 +78,8 @@ of the failing function and hide the other one::
|
||||||
|
|
||||||
test_module.py:9: AssertionError
|
test_module.py:9: AssertionError
|
||||||
----------------------------- Captured stdout ------------------------------
|
----------------------------- Captured stdout ------------------------------
|
||||||
setting up <function test_func2 at 0x228faa0>
|
setting up <function test_func2 at 0x2f27488>
|
||||||
==================== 1 failed, 1 passed in 0.02 seconds ====================
|
==================== 1 failed, 1 passed in 0.01 seconds ====================
|
||||||
|
|
||||||
Accessing captured output from a test function
|
Accessing captured output from a test function
|
||||||
---------------------------------------------------
|
---------------------------------------------------
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
#
|
#
|
||||||
# The full version, including alpha/beta/rc tags.
|
# The full version, including alpha/beta/rc tags.
|
||||||
# The short X.Y version.
|
# The short X.Y version.
|
||||||
version = release = "2.3.0.dev18"
|
version = release = "2.3.0.dev19"
|
||||||
|
|
||||||
import sys, os
|
import sys, os
|
||||||
|
|
||||||
|
@ -70,6 +70,7 @@ copyright = u'2011, holger krekel et alii'
|
||||||
# List of patterns, relative to source directory, that match files and
|
# List of patterns, relative to source directory, that match files and
|
||||||
# directories to ignore when looking for source files.
|
# directories to ignore when looking for source files.
|
||||||
exclude_patterns = ['links.inc', '_build', 'naming20.txt', 'test/*',
|
exclude_patterns = ['links.inc', '_build', 'naming20.txt', 'test/*',
|
||||||
|
"old_*",
|
||||||
'example/attic.txt',
|
'example/attic.txt',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -270,7 +271,7 @@ epub_copyright = u'2011, holger krekel et alii'
|
||||||
|
|
||||||
# Example configuration for intersphinx: refer to the Python standard library.
|
# Example configuration for intersphinx: refer to the Python standard library.
|
||||||
intersphinx_mapping = {'python': ('http://docs.python.org/', None),
|
intersphinx_mapping = {'python': ('http://docs.python.org/', None),
|
||||||
'lib': ("http://docs.python.org/library/", None),
|
# 'lib': ("http://docs.python.org/2.7library/", None),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -17,12 +17,15 @@ Full pytest documentation
|
||||||
plugins
|
plugins
|
||||||
talks
|
talks
|
||||||
develop
|
develop
|
||||||
|
funcarg_compare.txt
|
||||||
announce/index
|
announce/index
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:hidden:
|
:hidden:
|
||||||
|
|
||||||
changelog.txt
|
changelog.txt
|
||||||
resources
|
funcargs
|
||||||
example/resources_attic
|
example/resources_attic
|
||||||
|
setup.txt
|
||||||
|
example/remoteinterp.txt
|
||||||
|
|
||||||
|
|
|
@ -44,10 +44,9 @@ then you can just invoke ``py.test`` without command line options::
|
||||||
|
|
||||||
$ py.test
|
$ py.test
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev2
|
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19
|
||||||
plugins: xdist, bugzilla, cache, oejskit, pep8, cov
|
collected 1 items
|
||||||
collecting ... collected 1 items
|
|
||||||
|
|
||||||
mymodule.py .
|
mymodule.py .
|
||||||
|
|
||||||
========================= 1 passed in 0.07 seconds =========================
|
========================= 1 passed in 0.02 seconds =========================
|
||||||
|
|
|
@ -16,7 +16,6 @@ need more examples or have questions. Also take a look at the :ref:`comprehensiv
|
||||||
|
|
||||||
reportingdemo.txt
|
reportingdemo.txt
|
||||||
simple.txt
|
simple.txt
|
||||||
mysetup.txt
|
|
||||||
parametrize.txt
|
parametrize.txt
|
||||||
markers.txt
|
markers.txt
|
||||||
pythoncollection.txt
|
pythoncollection.txt
|
||||||
|
|
|
@ -26,9 +26,7 @@ You can then restrict a test run to only run tests marked with ``webtest``::
|
||||||
|
|
||||||
$ py.test -v -m webtest
|
$ py.test -v -m webtest
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev11 -- /home/hpk/venv/1/bin/python
|
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 -- /home/hpk/p/pytest/.tox/regen/bin/python
|
||||||
cachedir: /home/hpk/tmp/doc-exec-426/.cache
|
|
||||||
plugins: xdist, bugzilla, cache, oejskit, cli, timeout, pep8, cov
|
|
||||||
collecting ... collected 2 items
|
collecting ... collected 2 items
|
||||||
|
|
||||||
test_server.py:3: test_send_http PASSED
|
test_server.py:3: test_send_http PASSED
|
||||||
|
@ -40,15 +38,13 @@ Or the inverse, running all tests except the webtest ones::
|
||||||
|
|
||||||
$ py.test -v -m "not webtest"
|
$ py.test -v -m "not webtest"
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev11 -- /home/hpk/venv/1/bin/python
|
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 -- /home/hpk/p/pytest/.tox/regen/bin/python
|
||||||
cachedir: /home/hpk/tmp/doc-exec-426/.cache
|
|
||||||
plugins: xdist, bugzilla, cache, oejskit, cli, timeout, pep8, cov
|
|
||||||
collecting ... collected 2 items
|
collecting ... collected 2 items
|
||||||
|
|
||||||
test_server.py:6: test_something_quick PASSED
|
test_server.py:6: test_something_quick PASSED
|
||||||
|
|
||||||
================= 1 tests deselected by "-m 'not webtest'" =================
|
================= 1 tests deselected by "-m 'not webtest'" =================
|
||||||
================== 1 passed, 1 deselected in 0.01 seconds ==================
|
================== 1 passed, 1 deselected in 0.00 seconds ==================
|
||||||
|
|
||||||
Registering markers
|
Registering markers
|
||||||
-------------------------------------
|
-------------------------------------
|
||||||
|
@ -69,8 +65,6 @@ You can ask which markers exist for your test suite - the list includes our just
|
||||||
$ py.test --markers
|
$ py.test --markers
|
||||||
@pytest.mark.webtest: mark a test as a webtest.
|
@pytest.mark.webtest: mark a test as a webtest.
|
||||||
|
|
||||||
@pytest.mark.timeout(timeout, method=None): Set a timeout and timeout method on just one test item. The first argument, *timeout*, is the timeout in seconds while the keyword, *method*, takes the same values as the --timeout_method option.
|
|
||||||
|
|
||||||
@pytest.mark.skipif(*conditions): skip the given test function if evaluation of all conditions has a True value. Evaluation happens within the module global context. Example: skipif('sys.platform == "win32"') skips the test if we are on the win32 platform.
|
@pytest.mark.skipif(*conditions): skip the given test function if evaluation of all conditions has a True value. Evaluation happens within the module global context. Example: skipif('sys.platform == "win32"') skips the test if we are on the win32 platform.
|
||||||
|
|
||||||
@pytest.mark.xfail(*conditions, reason=None, run=True): mark the the test function as an expected failure. Optionally specify a reason and run=False if you don't even want to execute the test function. Any positional condition strings will be evaluated (like with skipif) and if one is False the marker will not be applied.
|
@pytest.mark.xfail(*conditions, reason=None, run=True): mark the the test function as an expected failure. Optionally specify a reason and run=False if you don't even want to execute the test function. Any positional condition strings will be evaluated (like with skipif) and if one is False the marker will not be applied.
|
||||||
|
@ -149,41 +143,38 @@ the given argument::
|
||||||
|
|
||||||
$ py.test -k send_http # running with the above defined examples
|
$ py.test -k send_http # running with the above defined examples
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev11
|
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19
|
||||||
plugins: xdist, bugzilla, cache, oejskit, cli, timeout, pep8, cov
|
collected 4 items
|
||||||
collecting ... collected 4 items
|
|
||||||
|
|
||||||
test_server.py .
|
test_server.py .
|
||||||
|
|
||||||
=================== 3 tests deselected by '-ksend_http' ====================
|
=================== 3 tests deselected by '-ksend_http' ====================
|
||||||
================== 1 passed, 3 deselected in 0.02 seconds ==================
|
================== 1 passed, 3 deselected in 0.01 seconds ==================
|
||||||
|
|
||||||
And you can also run all tests except the ones that match the keyword::
|
And you can also run all tests except the ones that match the keyword::
|
||||||
|
|
||||||
$ py.test -k-send_http
|
$ py.test -k-send_http
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev11
|
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19
|
||||||
plugins: xdist, bugzilla, cache, oejskit, cli, timeout, pep8, cov
|
collected 4 items
|
||||||
collecting ... collected 4 items
|
|
||||||
|
|
||||||
test_mark_classlevel.py ..
|
test_mark_classlevel.py ..
|
||||||
test_server.py .
|
test_server.py .
|
||||||
|
|
||||||
=================== 1 tests deselected by '-k-send_http' ===================
|
=================== 1 tests deselected by '-k-send_http' ===================
|
||||||
================== 3 passed, 1 deselected in 0.02 seconds ==================
|
================== 3 passed, 1 deselected in 0.01 seconds ==================
|
||||||
|
|
||||||
Or to only select the class::
|
Or to only select the class::
|
||||||
|
|
||||||
$ py.test -kTestClass
|
$ py.test -kTestClass
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev11
|
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19
|
||||||
plugins: xdist, bugzilla, cache, oejskit, cli, timeout, pep8, cov
|
collected 4 items
|
||||||
collecting ... collected 4 items
|
|
||||||
|
|
||||||
test_mark_classlevel.py ..
|
test_mark_classlevel.py ..
|
||||||
|
|
||||||
=================== 2 tests deselected by '-kTestClass' ====================
|
=================== 2 tests deselected by '-kTestClass' ====================
|
||||||
================== 2 passed, 2 deselected in 0.02 seconds ==================
|
================== 2 passed, 2 deselected in 0.01 seconds ==================
|
||||||
|
|
||||||
.. _`adding a custom marker from a plugin`:
|
.. _`adding a custom marker from a plugin`:
|
||||||
|
|
||||||
|
@ -230,33 +221,29 @@ the test needs::
|
||||||
|
|
||||||
$ py.test -E stage2
|
$ py.test -E stage2
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev11
|
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19
|
||||||
plugins: xdist, bugzilla, cache, oejskit, cli, timeout, pep8, cov
|
collected 1 items
|
||||||
collecting ... collected 1 items
|
|
||||||
|
|
||||||
test_someenv.py s
|
test_someenv.py s
|
||||||
|
|
||||||
======================== 1 skipped in 0.01 seconds =========================
|
======================== 1 skipped in 0.00 seconds =========================
|
||||||
|
|
||||||
and here is one that specifies exactly the environment needed::
|
and here is one that specifies exactly the environment needed::
|
||||||
|
|
||||||
$ py.test -E stage1
|
$ py.test -E stage1
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev11
|
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19
|
||||||
plugins: xdist, bugzilla, cache, oejskit, cli, timeout, pep8, cov
|
collected 1 items
|
||||||
collecting ... collected 1 items
|
|
||||||
|
|
||||||
test_someenv.py .
|
test_someenv.py .
|
||||||
|
|
||||||
========================= 1 passed in 0.01 seconds =========================
|
========================= 1 passed in 0.00 seconds =========================
|
||||||
|
|
||||||
The ``--markers`` option always gives you a list of available markers::
|
The ``--markers`` option always gives you a list of available markers::
|
||||||
|
|
||||||
$ py.test --markers
|
$ py.test --markers
|
||||||
@pytest.mark.env(name): mark test to run only on named environment
|
@pytest.mark.env(name): mark test to run only on named environment
|
||||||
|
|
||||||
@pytest.mark.timeout(timeout, method=None): Set a timeout and timeout method on just one test item. The first argument, *timeout*, is the timeout in seconds while the keyword, *method*, takes the same values as the --timeout_method option.
|
|
||||||
|
|
||||||
@pytest.mark.skipif(*conditions): skip the given test function if evaluation of all conditions has a True value. Evaluation happens within the module global context. Example: skipif('sys.platform == "win32"') skips the test if we are on the win32 platform.
|
@pytest.mark.skipif(*conditions): skip the given test function if evaluation of all conditions has a True value. Evaluation happens within the module global context. Example: skipif('sys.platform == "win32"') skips the test if we are on the win32 platform.
|
||||||
|
|
||||||
@pytest.mark.xfail(*conditions, reason=None, run=True): mark the the test function as an expected failure. Optionally specify a reason and run=False if you don't even want to execute the test function. Any positional condition strings will be evaluated (like with skipif) and if one is False the marker will not be applied.
|
@pytest.mark.xfail(*conditions, reason=None, run=True): mark the the test function as an expected failure. Optionally specify a reason and run=False if you don't even want to execute the test function. Any positional condition strings will be evaluated (like with skipif) and if one is False the marker will not be applied.
|
||||||
|
@ -304,12 +291,10 @@ test function. From a conftest file we can read it like this::
|
||||||
Let's run this without capturing output and see what we get::
|
Let's run this without capturing output and see what we get::
|
||||||
|
|
||||||
$ py.test -q -s
|
$ py.test -q -s
|
||||||
collecting ... collected 1 items
|
|
||||||
glob args=('function',) kwargs={'x': 3}
|
glob args=('function',) kwargs={'x': 3}
|
||||||
glob args=('class',) kwargs={'x': 2}
|
glob args=('class',) kwargs={'x': 2}
|
||||||
glob args=('module',) kwargs={'x': 1}
|
glob args=('module',) kwargs={'x': 1}
|
||||||
.
|
.
|
||||||
1 passed in 0.01 seconds
|
|
||||||
|
|
||||||
marking platform specific tests with pytest
|
marking platform specific tests with pytest
|
||||||
--------------------------------------------------------------
|
--------------------------------------------------------------
|
||||||
|
@ -362,23 +347,21 @@ then you will see two test skipped and two executed tests as expected::
|
||||||
|
|
||||||
$ py.test -rs # this option reports skip reasons
|
$ py.test -rs # this option reports skip reasons
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev11
|
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19
|
||||||
plugins: xdist, bugzilla, cache, oejskit, cli, timeout, pep8, cov
|
collected 4 items
|
||||||
collecting ... collected 4 items
|
|
||||||
|
|
||||||
test_plat.py s.s.
|
test_plat.py s.s.
|
||||||
========================= short test summary info ==========================
|
========================= short test summary info ==========================
|
||||||
SKIP [2] /home/hpk/tmp/doc-exec-426/conftest.py:12: cannot run on platform linux2
|
SKIP [2] /tmp/doc-exec-257/conftest.py:12: cannot run on platform linux2
|
||||||
|
|
||||||
=================== 2 passed, 2 skipped in 0.02 seconds ====================
|
=================== 2 passed, 2 skipped in 0.01 seconds ====================
|
||||||
|
|
||||||
Note that if you specify a platform via the marker-command line option like this::
|
Note that if you specify a platform via the marker-command line option like this::
|
||||||
|
|
||||||
$ py.test -m linux2
|
$ py.test -m linux2
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev11
|
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19
|
||||||
plugins: xdist, bugzilla, cache, oejskit, cli, timeout, pep8, cov
|
collected 4 items
|
||||||
collecting ... collected 4 items
|
|
||||||
|
|
||||||
test_plat.py .
|
test_plat.py .
|
||||||
|
|
||||||
|
|
|
@ -1,142 +0,0 @@
|
||||||
|
|
||||||
.. highlightlang:: python
|
|
||||||
|
|
||||||
.. _mysetup:
|
|
||||||
|
|
||||||
Mysetup pattern: application specific test fixtures
|
|
||||||
==========================================================
|
|
||||||
|
|
||||||
Here is a basic useful step-by-step example for managing and interacting
|
|
||||||
with application specific test setup. The goal is to have one place
|
|
||||||
where we have the glue and test support code for bootstrapping and
|
|
||||||
configuring application objects and allow test modules and test
|
|
||||||
functions to stay ignorant of involved details.
|
|
||||||
|
|
||||||
Step 1: Implementing the test/app-specific ``mysetup`` pattern
|
|
||||||
--------------------------------------------------------------
|
|
||||||
|
|
||||||
Let's write a simple test function using a ``mysetup`` funcarg::
|
|
||||||
|
|
||||||
# content of test_sample.py
|
|
||||||
def test_answer(mysetup):
|
|
||||||
app = mysetup.myapp()
|
|
||||||
answer = app.question()
|
|
||||||
assert answer == 42
|
|
||||||
|
|
||||||
To run this test py.test needs to find and call a factory to
|
|
||||||
obtain the required ``mysetup`` function argument. To make
|
|
||||||
an according factory findable we write down a specifically named factory
|
|
||||||
method in a :ref:`local plugin <localplugin>` ::
|
|
||||||
|
|
||||||
# content of conftest.py
|
|
||||||
from myapp import MyApp
|
|
||||||
|
|
||||||
def pytest_funcarg__mysetup(request): # "mysetup" factory function
|
|
||||||
return MySetup()
|
|
||||||
|
|
||||||
class MySetup: # instances of this are seen by test functions
|
|
||||||
def myapp(self):
|
|
||||||
return MyApp()
|
|
||||||
|
|
||||||
To run the example we stub out a simple ``MyApp`` application object::
|
|
||||||
|
|
||||||
# content of myapp.py
|
|
||||||
class MyApp:
|
|
||||||
def question(self):
|
|
||||||
return 6 * 9
|
|
||||||
|
|
||||||
You can now run the test::
|
|
||||||
|
|
||||||
$ py.test test_sample.py
|
|
||||||
=========================== test session starts ============================
|
|
||||||
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev2
|
|
||||||
plugins: xdist, bugzilla, cache, oejskit, pep8, cov
|
|
||||||
collecting ... collected 1 items
|
|
||||||
|
|
||||||
test_sample.py F
|
|
||||||
|
|
||||||
================================= FAILURES =================================
|
|
||||||
_______________________________ test_answer ________________________________
|
|
||||||
|
|
||||||
mysetup = <conftest.MySetup instance at 0x27e5320>
|
|
||||||
|
|
||||||
def test_answer(mysetup):
|
|
||||||
app = mysetup.myapp()
|
|
||||||
answer = app.question()
|
|
||||||
> assert answer == 42
|
|
||||||
E assert 54 == 42
|
|
||||||
|
|
||||||
test_sample.py:4: AssertionError
|
|
||||||
========================= 1 failed in 0.02 seconds =========================
|
|
||||||
|
|
||||||
This means that our ``mysetup`` object was successfully instantiated
|
|
||||||
and ``mysetup.app()`` returned an initialized ``MyApp`` instance.
|
|
||||||
We can ask it about the question and if you are confused as to what
|
|
||||||
the concrete question or answers actually mean, please see here_.
|
|
||||||
|
|
||||||
.. _here: http://uncyclopedia.wikia.com/wiki/The_Hitchhiker's_Guide_to_the_Galaxy
|
|
||||||
.. _`tut-cmdlineoption`:
|
|
||||||
|
|
||||||
Step 2: Checking a command line option and skipping tests
|
|
||||||
-----------------------------------------------------------
|
|
||||||
|
|
||||||
To add a command line option we update the ``conftest.py`` of
|
|
||||||
the previous example to add a command line option
|
|
||||||
and to offer a new mysetup method::
|
|
||||||
|
|
||||||
# content of ./conftest.py
|
|
||||||
import pytest
|
|
||||||
from myapp import MyApp
|
|
||||||
|
|
||||||
def pytest_funcarg__mysetup(request): # "mysetup" factory function
|
|
||||||
return MySetup(request)
|
|
||||||
|
|
||||||
def pytest_addoption(parser):
|
|
||||||
parser.addoption("--ssh", action="store", default=None,
|
|
||||||
help="specify ssh host to run tests with")
|
|
||||||
|
|
||||||
|
|
||||||
class MySetup:
|
|
||||||
def __init__(self, request):
|
|
||||||
self.config = request.config
|
|
||||||
|
|
||||||
def myapp(self):
|
|
||||||
return MyApp()
|
|
||||||
|
|
||||||
def getsshconnection(self):
|
|
||||||
host = self.config.option.ssh
|
|
||||||
if host is None:
|
|
||||||
pytest.skip("specify ssh host with --ssh")
|
|
||||||
return execnet.SshGateway(host)
|
|
||||||
|
|
||||||
|
|
||||||
Now any test function can use the ``mysetup.getsshconnection()`` method
|
|
||||||
like this::
|
|
||||||
|
|
||||||
# content of test_ssh.py
|
|
||||||
class TestClass:
|
|
||||||
def test_function(self, mysetup):
|
|
||||||
conn = mysetup.getsshconnection()
|
|
||||||
# work with conn
|
|
||||||
|
|
||||||
Running it yields::
|
|
||||||
|
|
||||||
$ py.test test_ssh.py -rs
|
|
||||||
=========================== test session starts ============================
|
|
||||||
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev2
|
|
||||||
plugins: xdist, bugzilla, cache, oejskit, pep8, cov
|
|
||||||
collecting ... collected 1 items
|
|
||||||
|
|
||||||
test_ssh.py s
|
|
||||||
========================= short test summary info ==========================
|
|
||||||
SKIP [1] /home/hpk/tmp/doc-exec-306/conftest.py:22: specify ssh host with --ssh
|
|
||||||
|
|
||||||
======================== 1 skipped in 0.02 seconds =========================
|
|
||||||
|
|
||||||
If you specify a command line option like ``py.test --ssh=python.org`` the test will execute as expected.
|
|
||||||
|
|
||||||
Note that neither the ``TestClass`` nor the ``test_function`` need to
|
|
||||||
know anything about how to setup the test state. It is handled separately
|
|
||||||
in your "test setup glue" code in the ``conftest.py`` file. It is easy
|
|
||||||
to extend the ``mysetup`` object for further needs in the test code - and for use by any other test functions in the files and directories below the ``conftest.py`` file.
|
|
||||||
|
|
|
@ -27,18 +27,15 @@ now execute the test specification::
|
||||||
|
|
||||||
nonpython $ py.test test_simple.yml
|
nonpython $ py.test test_simple.yml
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev2
|
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19
|
||||||
plugins: xdist, bugzilla, cache, oejskit, pep8, cov
|
collected 0 items / 1 errors
|
||||||
collecting ... collected 2 items
|
|
||||||
|
|
||||||
test_simple.yml .F
|
================================== ERRORS ==================================
|
||||||
|
_____________________ ERROR collecting test_simple.yml _____________________
|
||||||
================================= FAILURES =================================
|
conftest.py:11: in collect
|
||||||
______________________________ usecase: hello ______________________________
|
> import yaml # we need a yaml parser, e.g. PyYAML
|
||||||
usecase execution failed
|
E ImportError: No module named yaml
|
||||||
spec failed: 'some': 'other'
|
========================= 1 error in 0.00 seconds ==========================
|
||||||
no further details known at this point.
|
|
||||||
==================== 1 failed, 1 passed in 0.11 seconds ====================
|
|
||||||
|
|
||||||
You get one dot for the passing ``sub1: sub1`` check and one failure.
|
You get one dot for the passing ``sub1: sub1`` check and one failure.
|
||||||
Obviously in the above ``conftest.py`` you'll want to implement a more
|
Obviously in the above ``conftest.py`` you'll want to implement a more
|
||||||
|
@ -57,31 +54,27 @@ consulted when reporting in ``verbose`` mode::
|
||||||
|
|
||||||
nonpython $ py.test -v
|
nonpython $ py.test -v
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev2 -- /home/hpk/venv/1/bin/python
|
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 -- /home/hpk/p/pytest/.tox/regen/bin/python
|
||||||
cachedir: /home/hpk/p/pytest/doc/en/.cache
|
collecting ... collected 0 items / 1 errors
|
||||||
plugins: xdist, bugzilla, cache, oejskit, pep8, cov
|
|
||||||
collecting ... collected 2 items
|
|
||||||
|
|
||||||
test_simple.yml:1: usecase: ok PASSED
|
================================== ERRORS ==================================
|
||||||
test_simple.yml:1: usecase: hello FAILED
|
_____________________ ERROR collecting test_simple.yml _____________________
|
||||||
|
conftest.py:11: in collect
|
||||||
================================= FAILURES =================================
|
> import yaml # we need a yaml parser, e.g. PyYAML
|
||||||
______________________________ usecase: hello ______________________________
|
E ImportError: No module named yaml
|
||||||
usecase execution failed
|
========================= 1 error in 0.01 seconds ==========================
|
||||||
spec failed: 'some': 'other'
|
|
||||||
no further details known at this point.
|
|
||||||
==================== 1 failed, 1 passed in 0.04 seconds ====================
|
|
||||||
|
|
||||||
While developing your custom test collection and execution it's also
|
While developing your custom test collection and execution it's also
|
||||||
interesting to just look at the collection tree::
|
interesting to just look at the collection tree::
|
||||||
|
|
||||||
nonpython $ py.test --collectonly
|
nonpython $ py.test --collectonly
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev2
|
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19
|
||||||
plugins: xdist, bugzilla, cache, oejskit, pep8, cov
|
collected 0 items / 1 errors
|
||||||
collecting ... collected 2 items
|
|
||||||
<YamlFile 'test_simple.yml'>
|
|
||||||
<YamlItem 'ok'>
|
|
||||||
<YamlItem 'hello'>
|
|
||||||
|
|
||||||
============================= in 0.04 seconds =============================
|
================================== ERRORS ==================================
|
||||||
|
_____________________ ERROR collecting test_simple.yml _____________________
|
||||||
|
conftest.py:11: in collect
|
||||||
|
> import yaml # we need a yaml parser, e.g. PyYAML
|
||||||
|
E ImportError: No module named yaml
|
||||||
|
========================= 1 error in 0.01 seconds ==========================
|
||||||
|
|
|
@ -36,7 +36,6 @@ we parametrize two arguments of the test function so that the test
|
||||||
function is called three times. Let's run it::
|
function is called three times. Let's run it::
|
||||||
|
|
||||||
$ py.test -q
|
$ py.test -q
|
||||||
collecting ... collected 3 items
|
|
||||||
..F
|
..F
|
||||||
================================= FAILURES =================================
|
================================= FAILURES =================================
|
||||||
____________________________ test_eval[6*9-42] _____________________________
|
____________________________ test_eval[6*9-42] _____________________________
|
||||||
|
@ -54,7 +53,6 @@ function is called three times. Let's run it::
|
||||||
E + where 54 = eval('6*9')
|
E + where 54 = eval('6*9')
|
||||||
|
|
||||||
test_expectation.py:8: AssertionError
|
test_expectation.py:8: AssertionError
|
||||||
1 failed, 2 passed in 0.02 seconds
|
|
||||||
|
|
||||||
As expected only one pair of input/output values fails the simple test function.
|
As expected only one pair of input/output values fails the simple test function.
|
||||||
|
|
||||||
|
@ -94,15 +92,12 @@ Now we add a test configuration like this::
|
||||||
This means that we only run 2 tests if we do not pass ``--all``::
|
This means that we only run 2 tests if we do not pass ``--all``::
|
||||||
|
|
||||||
$ py.test -q test_compute.py
|
$ py.test -q test_compute.py
|
||||||
collecting ... collected 2 items
|
|
||||||
..
|
..
|
||||||
2 passed in 0.01 seconds
|
|
||||||
|
|
||||||
We run only two computations, so we see two dots.
|
We run only two computations, so we see two dots.
|
||||||
let's run the full monty::
|
let's run the full monty::
|
||||||
|
|
||||||
$ py.test -q --all
|
$ py.test -q --all
|
||||||
collecting ... collected 5 items
|
|
||||||
....F
|
....F
|
||||||
================================= FAILURES =================================
|
================================= FAILURES =================================
|
||||||
_____________________________ test_compute[4] ______________________________
|
_____________________________ test_compute[4] ______________________________
|
||||||
|
@ -114,7 +109,6 @@ let's run the full monty::
|
||||||
E assert 4 < 4
|
E assert 4 < 4
|
||||||
|
|
||||||
test_compute.py:3: AssertionError
|
test_compute.py:3: AssertionError
|
||||||
1 failed, 4 passed in 0.02 seconds
|
|
||||||
|
|
||||||
As expected when running the full range of ``param1`` values
|
As expected when running the full range of ``param1`` values
|
||||||
we'll get an error on the last one.
|
we'll get an error on the last one.
|
||||||
|
@ -157,22 +151,20 @@ this is a fully self-contained example which you can run with::
|
||||||
|
|
||||||
$ py.test test_scenarios.py
|
$ py.test test_scenarios.py
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev14
|
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19
|
||||||
plugins: xdist, bugzilla, cache, oejskit, cli, timeout, pep8, cov
|
collected 4 items
|
||||||
collecting ... collected 4 items
|
|
||||||
|
|
||||||
test_scenarios.py ....
|
test_scenarios.py ....
|
||||||
|
|
||||||
========================= 4 passed in 0.02 seconds =========================
|
========================= 4 passed in 0.01 seconds =========================
|
||||||
|
|
||||||
If you just collect tests you'll also nicely see 'advanced' and 'basic' as variants for the test function::
|
If you just collect tests you'll also nicely see 'advanced' and 'basic' as variants for the test function::
|
||||||
|
|
||||||
|
|
||||||
$ py.test --collectonly test_scenarios.py
|
$ py.test --collectonly test_scenarios.py
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev14
|
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19
|
||||||
plugins: xdist, bugzilla, cache, oejskit, cli, timeout, pep8, cov
|
collected 4 items
|
||||||
collecting ... collected 4 items
|
|
||||||
<Module 'test_scenarios.py'>
|
<Module 'test_scenarios.py'>
|
||||||
<Class 'TestSampleWithScenarios'>
|
<Class 'TestSampleWithScenarios'>
|
||||||
<Instance '()'>
|
<Instance '()'>
|
||||||
|
@ -233,24 +225,22 @@ Let's first see how it looks like at collection time::
|
||||||
|
|
||||||
$ py.test test_backends.py --collectonly
|
$ py.test test_backends.py --collectonly
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev14
|
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19
|
||||||
plugins: xdist, bugzilla, cache, oejskit, cli, timeout, pep8, cov
|
collected 2 items
|
||||||
collecting ... collected 2 items
|
|
||||||
<Module 'test_backends.py'>
|
<Module 'test_backends.py'>
|
||||||
<Function 'test_db_initialized[d1]'>
|
<Function 'test_db_initialized[d1]'>
|
||||||
<Function 'test_db_initialized[d2]'>
|
<Function 'test_db_initialized[d2]'>
|
||||||
|
|
||||||
============================= in 0.01 seconds =============================
|
============================= in 0.00 seconds =============================
|
||||||
|
|
||||||
And then when we run the test::
|
And then when we run the test::
|
||||||
|
|
||||||
$ py.test -q test_backends.py
|
$ py.test -q test_backends.py
|
||||||
collecting ... collected 2 items
|
|
||||||
.F
|
.F
|
||||||
================================= FAILURES =================================
|
================================= FAILURES =================================
|
||||||
_________________________ test_db_initialized[d2] __________________________
|
_________________________ test_db_initialized[d2] __________________________
|
||||||
|
|
||||||
db = <conftest.DB2 instance at 0x17dd440>
|
db = <conftest.DB2 instance at 0x13dc9e0>
|
||||||
|
|
||||||
def test_db_initialized(db):
|
def test_db_initialized(db):
|
||||||
# a dummy test
|
# a dummy test
|
||||||
|
@ -259,7 +249,6 @@ And then when we run the test::
|
||||||
E Failed: deliberately failing for demo purposes
|
E Failed: deliberately failing for demo purposes
|
||||||
|
|
||||||
test_backends.py:6: Failed
|
test_backends.py:6: Failed
|
||||||
1 failed, 1 passed in 0.01 seconds
|
|
||||||
|
|
||||||
The first invocation with ``db == "DB1"`` passed while the second with ``db == "DB2"`` failed. Our ``pytest_funcarg__db`` factory has instantiated each of the DB values during the setup phase while the ``pytest_generate_tests`` generated two according calls to the ``test_db_initialized`` during the collection phase.
|
The first invocation with ``db == "DB1"`` passed while the second with ``db == "DB2"`` failed. Our ``pytest_funcarg__db`` factory has instantiated each of the DB values during the setup phase while the ``pytest_generate_tests`` generated two according calls to the ``test_db_initialized`` during the collection phase.
|
||||||
|
|
||||||
|
@ -302,19 +291,17 @@ Our test generator looks up a class-level definition which specifies which
|
||||||
argument sets to use for each test function. Let's run it::
|
argument sets to use for each test function. Let's run it::
|
||||||
|
|
||||||
$ py.test -q
|
$ py.test -q
|
||||||
collecting ... collected 3 items
|
|
||||||
F..
|
F..
|
||||||
================================= FAILURES =================================
|
================================= FAILURES =================================
|
||||||
________________________ TestClass.test_equals[1-2] ________________________
|
________________________ TestClass.test_equals[1-2] ________________________
|
||||||
|
|
||||||
self = <test_parametrize.TestClass instance at 0x19a6d88>, a = 1, b = 2
|
self = <test_parametrize.TestClass instance at 0x23ea170>, a = 1, b = 2
|
||||||
|
|
||||||
def test_equals(self, a, b):
|
def test_equals(self, a, b):
|
||||||
> assert a == b
|
> assert a == b
|
||||||
E assert 1 == 2
|
E assert 1 == 2
|
||||||
|
|
||||||
test_parametrize.py:18: AssertionError
|
test_parametrize.py:18: AssertionError
|
||||||
1 failed, 2 passed in 0.02 seconds
|
|
||||||
|
|
||||||
Indirect parametrization with multiple resources
|
Indirect parametrization with multiple resources
|
||||||
--------------------------------------------------------------
|
--------------------------------------------------------------
|
||||||
|
@ -333,8 +320,6 @@ with different sets of arguments for its three arguments:
|
||||||
Running it results in some skips if we don't have all the python interpreters installed and otherwise runs all combinations (5 interpreters times 5 interpreters times 3 objects to serialize/deserialize)::
|
Running it results in some skips if we don't have all the python interpreters installed and otherwise runs all combinations (5 interpreters times 5 interpreters times 3 objects to serialize/deserialize)::
|
||||||
|
|
||||||
. $ py.test -rs -q multipython.py
|
. $ py.test -rs -q multipython.py
|
||||||
collecting ... collected 75 items
|
|
||||||
............sss............sss............sss............ssssssssssssssssss
|
............sss............sss............sss............ssssssssssssssssss
|
||||||
========================= short test summary info ==========================
|
========================= short test summary info ==========================
|
||||||
SKIP [27] /home/hpk/p/pytest/doc/en/example/multipython.py:21: 'python2.8' not found
|
SKIP [27] /home/hpk/p/pytest/doc/en/example/multipython.py:21: 'python2.8' not found
|
||||||
48 passed, 27 skipped in 3.11 seconds
|
|
||||||
|
|
|
@ -43,8 +43,8 @@ then the test collection looks like this::
|
||||||
|
|
||||||
$ py.test --collectonly
|
$ py.test --collectonly
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux2 -- Python 2.7.3 -- pytest-2.2.5.dev1
|
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19
|
||||||
collecting ... collected 2 items
|
collected 2 items
|
||||||
<Module 'check_myapp.py'>
|
<Module 'check_myapp.py'>
|
||||||
<Class 'CheckMyApp'>
|
<Class 'CheckMyApp'>
|
||||||
<Instance '()'>
|
<Instance '()'>
|
||||||
|
@ -82,8 +82,8 @@ You can always peek at the collection tree without running tests like this::
|
||||||
|
|
||||||
. $ py.test --collectonly pythoncollection.py
|
. $ py.test --collectonly pythoncollection.py
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux2 -- Python 2.7.3 -- pytest-2.2.5.dev1
|
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19
|
||||||
collecting ... collected 3 items
|
collected 3 items
|
||||||
<Module 'pythoncollection.py'>
|
<Module 'pythoncollection.py'>
|
||||||
<Function 'test_function'>
|
<Function 'test_function'>
|
||||||
<Class 'TestClass'>
|
<Class 'TestClass'>
|
||||||
|
@ -135,8 +135,8 @@ interpreters and will leave out the setup.py file::
|
||||||
|
|
||||||
$ py.test --collectonly
|
$ py.test --collectonly
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux2 -- Python 2.7.3 -- pytest-2.2.5.dev1
|
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19
|
||||||
collecting ... collected 1 items
|
collected 1 items
|
||||||
<Module 'pkg/module_py2.py'>
|
<Module 'pkg/module_py2.py'>
|
||||||
<Function 'test_only_on_python2'>
|
<Function 'test_only_on_python2'>
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,198 @@
|
||||||
|
|
||||||
|
.. highlightlang:: python
|
||||||
|
|
||||||
|
.. _myapp:
|
||||||
|
|
||||||
|
Building an SSH connecting Application fixture
|
||||||
|
==========================================================
|
||||||
|
|
||||||
|
The goal of this tutorial-example is to show how you can put efficient
|
||||||
|
test support and fixture code in one place, allowing test modules and
|
||||||
|
test functions to stay ignorant of importing, configuration or
|
||||||
|
setup/teardown details.
|
||||||
|
|
||||||
|
The tutorial implements a simple ``RemoteInterpreter`` object that
|
||||||
|
allows evaluation of python expressions. We are going to use
|
||||||
|
the `execnet <http://codespeak.net/execnet>`_ package for the
|
||||||
|
underlying cross-python bridge functionality.
|
||||||
|
|
||||||
|
|
||||||
|
Step 1: Implementing a first test
|
||||||
|
--------------------------------------------------------------
|
||||||
|
|
||||||
|
Let's write a simple test function using a not yet defined ``interp`` fixture::
|
||||||
|
|
||||||
|
# content of test_remoteinterpreter.py
|
||||||
|
|
||||||
|
def test_eval_simple(interp):
|
||||||
|
assert interp.eval("6*9") == 42
|
||||||
|
|
||||||
|
The test function needs an argument named `interp` and therefore pytest will
|
||||||
|
look for a :ref:`fixture function` that matches this name. We'll define it
|
||||||
|
in a :ref:`local plugin <localplugin>` to make it available also to other
|
||||||
|
test modules::
|
||||||
|
|
||||||
|
# content of conftest.py
|
||||||
|
|
||||||
|
from remoteinterpreter import RemoteInterpreter
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def interp(request):
|
||||||
|
import execnet
|
||||||
|
gw = execnet.makegateway()
|
||||||
|
return RemoteInterpreter(gw)
|
||||||
|
|
||||||
|
To run the example we furthermore need to implement a RemoteInterpreter
|
||||||
|
object which working with the injected execnet-gateway connection::
|
||||||
|
|
||||||
|
# content of remoteintepreter.py
|
||||||
|
|
||||||
|
class RemoteInterpreter:
|
||||||
|
def __init__(self, gateway):
|
||||||
|
self.gateway = gateway
|
||||||
|
|
||||||
|
def eval(self, expression):
|
||||||
|
# execnet open a "gateway" to the remote process
|
||||||
|
# which enables to remotely execute code and communicate
|
||||||
|
# to and fro via channels
|
||||||
|
ch = self.gateway.remote_exec("channel.send(%s)" % expression)
|
||||||
|
return ch.receive()
|
||||||
|
|
||||||
|
That's it, we can now run the test::
|
||||||
|
|
||||||
|
$ py.test test_remoteinterpreter.py
|
||||||
|
Traceback (most recent call last):
|
||||||
|
File "/home/hpk/p/pytest/.tox/regen/bin/py.test", line 9, in <module>
|
||||||
|
load_entry_point('pytest==2.3.0.dev19', 'console_scripts', 'py.test')()
|
||||||
|
File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/core.py", line 469, in main
|
||||||
|
config = _prepareconfig(args, plugins)
|
||||||
|
File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/core.py", line 463, in _prepareconfig
|
||||||
|
pluginmanager=_pluginmanager, args=args)
|
||||||
|
File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/core.py", line 422, in __call__
|
||||||
|
return self._docall(methods, kwargs)
|
||||||
|
File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/core.py", line 433, in _docall
|
||||||
|
res = mc.execute()
|
||||||
|
File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/core.py", line 351, in execute
|
||||||
|
res = method(**kwargs)
|
||||||
|
File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/helpconfig.py", line 25, in pytest_cmdline_parse
|
||||||
|
config = __multicall__.execute()
|
||||||
|
File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/core.py", line 351, in execute
|
||||||
|
res = method(**kwargs)
|
||||||
|
File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 10, in pytest_cmdline_parse
|
||||||
|
config.parse(args)
|
||||||
|
File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 344, in parse
|
||||||
|
self._preparse(args)
|
||||||
|
File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 322, in _preparse
|
||||||
|
self._setinitialconftest(args)
|
||||||
|
File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 301, in _setinitialconftest
|
||||||
|
self._conftest.setinitial(args)
|
||||||
|
File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 160, in setinitial
|
||||||
|
self._try_load_conftest(anchor)
|
||||||
|
File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 166, in _try_load_conftest
|
||||||
|
self._path2confmods[None] = self.getconftestmodules(anchor)
|
||||||
|
File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 190, in getconftestmodules
|
||||||
|
clist[:0] = self.getconftestmodules(dp)
|
||||||
|
File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 189, in getconftestmodules
|
||||||
|
clist.append(self.importconftest(conftestpath))
|
||||||
|
File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 218, in importconftest
|
||||||
|
self._conftestpath2mod[conftestpath] = mod = conftestpath.pyimport()
|
||||||
|
File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/py/_path/local.py", line 532, in pyimport
|
||||||
|
__import__(modname)
|
||||||
|
File "/tmp/doc-exec-261/conftest.py", line 2, in <module>
|
||||||
|
from remoteinterpreter import RemoteInterpreter
|
||||||
|
ImportError: No module named remoteinterpreter
|
||||||
|
|
||||||
|
.. _`tut-cmdlineoption`:
|
||||||
|
|
||||||
|
Step 2: Adding command line configuration
|
||||||
|
-----------------------------------------------------------
|
||||||
|
|
||||||
|
To add a command line option we update the ``conftest.py`` of
|
||||||
|
the previous example and add a command line option which
|
||||||
|
is passed on to the MyApp object::
|
||||||
|
|
||||||
|
# content of ./conftest.py
|
||||||
|
import pytest
|
||||||
|
from myapp import MyApp
|
||||||
|
|
||||||
|
def pytest_addoption(parser): # pytest hook called during initialisation
|
||||||
|
parser.addoption("--ssh", action="store", default=None,
|
||||||
|
help="specify ssh host to run tests with")
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def mysetup(request): # "mysetup" factory function
|
||||||
|
return MySetup(request.config)
|
||||||
|
|
||||||
|
class MySetup:
|
||||||
|
def __init__(self, config):
|
||||||
|
self.config = config
|
||||||
|
self.app = MyApp()
|
||||||
|
|
||||||
|
def getsshconnection(self):
|
||||||
|
import execnet
|
||||||
|
host = self.config.option.ssh
|
||||||
|
if host is None:
|
||||||
|
pytest.skip("specify ssh host with --ssh")
|
||||||
|
return execnet.SshGateway(host)
|
||||||
|
|
||||||
|
|
||||||
|
Now any test function can use the ``mysetup.getsshconnection()`` method
|
||||||
|
like this::
|
||||||
|
|
||||||
|
# content of test_ssh.py
|
||||||
|
class TestClass:
|
||||||
|
def test_function(self, mysetup):
|
||||||
|
conn = mysetup.getsshconnection()
|
||||||
|
# work with conn
|
||||||
|
|
||||||
|
Running it yields::
|
||||||
|
|
||||||
|
$ py.test -q test_ssh.py -rs
|
||||||
|
Traceback (most recent call last):
|
||||||
|
File "/home/hpk/p/pytest/.tox/regen/bin/py.test", line 9, in <module>
|
||||||
|
load_entry_point('pytest==2.3.0.dev19', 'console_scripts', 'py.test')()
|
||||||
|
File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/core.py", line 469, in main
|
||||||
|
config = _prepareconfig(args, plugins)
|
||||||
|
File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/core.py", line 463, in _prepareconfig
|
||||||
|
pluginmanager=_pluginmanager, args=args)
|
||||||
|
File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/core.py", line 422, in __call__
|
||||||
|
return self._docall(methods, kwargs)
|
||||||
|
File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/core.py", line 433, in _docall
|
||||||
|
res = mc.execute()
|
||||||
|
File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/core.py", line 351, in execute
|
||||||
|
res = method(**kwargs)
|
||||||
|
File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/helpconfig.py", line 25, in pytest_cmdline_parse
|
||||||
|
config = __multicall__.execute()
|
||||||
|
File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/core.py", line 351, in execute
|
||||||
|
res = method(**kwargs)
|
||||||
|
File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 10, in pytest_cmdline_parse
|
||||||
|
config.parse(args)
|
||||||
|
File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 344, in parse
|
||||||
|
self._preparse(args)
|
||||||
|
File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 322, in _preparse
|
||||||
|
self._setinitialconftest(args)
|
||||||
|
File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 301, in _setinitialconftest
|
||||||
|
self._conftest.setinitial(args)
|
||||||
|
File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 160, in setinitial
|
||||||
|
self._try_load_conftest(anchor)
|
||||||
|
File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 166, in _try_load_conftest
|
||||||
|
self._path2confmods[None] = self.getconftestmodules(anchor)
|
||||||
|
File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 190, in getconftestmodules
|
||||||
|
clist[:0] = self.getconftestmodules(dp)
|
||||||
|
File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 189, in getconftestmodules
|
||||||
|
clist.append(self.importconftest(conftestpath))
|
||||||
|
File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 218, in importconftest
|
||||||
|
self._conftestpath2mod[conftestpath] = mod = conftestpath.pyimport()
|
||||||
|
File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/py/_path/local.py", line 532, in pyimport
|
||||||
|
__import__(modname)
|
||||||
|
File "/tmp/doc-exec-261/conftest.py", line 2, in <module>
|
||||||
|
from myapp import MyApp
|
||||||
|
ImportError: No module named myapp
|
||||||
|
|
||||||
|
If you specify a command line option like ``py.test --ssh=python.org`` the test will execute as expected.
|
||||||
|
|
||||||
|
Note that neither the ``TestClass`` nor the ``test_function`` need to
|
||||||
|
know anything about how to setup the test state. It is handled separately
|
||||||
|
in the ``conftest.py`` file. It is easy
|
||||||
|
to extend the ``mysetup`` object for further needs in the test code - and for use by any other test functions in the files and directories below the ``conftest.py`` file.
|
||||||
|
|
|
@ -13,8 +13,8 @@ get on the terminal - we are working on that):
|
||||||
|
|
||||||
assertion $ py.test failure_demo.py
|
assertion $ py.test failure_demo.py
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux2 -- Python 2.7.1 -- pytest-2.2.4
|
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19
|
||||||
collecting ... collected 39 items
|
collected 39 items
|
||||||
|
|
||||||
failure_demo.py FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
|
failure_demo.py FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ get on the terminal - we are working on that):
|
||||||
failure_demo.py:15: AssertionError
|
failure_demo.py:15: AssertionError
|
||||||
_________________________ TestFailing.test_simple __________________________
|
_________________________ TestFailing.test_simple __________________________
|
||||||
|
|
||||||
self = <failure_demo.TestFailing object at 0x2e4dd50>
|
self = <failure_demo.TestFailing object at 0x1dbc8d0>
|
||||||
|
|
||||||
def test_simple(self):
|
def test_simple(self):
|
||||||
def f():
|
def f():
|
||||||
|
@ -40,13 +40,13 @@ get on the terminal - we are working on that):
|
||||||
|
|
||||||
> assert f() == g()
|
> assert f() == g()
|
||||||
E assert 42 == 43
|
E assert 42 == 43
|
||||||
E + where 42 = <function f at 0x2e73c80>()
|
E + where 42 = <function f at 0x1d45230>()
|
||||||
E + and 43 = <function g at 0x2e73cf8>()
|
E + and 43 = <function g at 0x1d452a8>()
|
||||||
|
|
||||||
failure_demo.py:28: AssertionError
|
failure_demo.py:28: AssertionError
|
||||||
____________________ TestFailing.test_simple_multiline _____________________
|
____________________ TestFailing.test_simple_multiline _____________________
|
||||||
|
|
||||||
self = <failure_demo.TestFailing object at 0x2e4d7d0>
|
self = <failure_demo.TestFailing object at 0x1dbcdd0>
|
||||||
|
|
||||||
def test_simple_multiline(self):
|
def test_simple_multiline(self):
|
||||||
otherfunc_multi(
|
otherfunc_multi(
|
||||||
|
@ -66,19 +66,19 @@ get on the terminal - we are working on that):
|
||||||
failure_demo.py:11: AssertionError
|
failure_demo.py:11: AssertionError
|
||||||
___________________________ TestFailing.test_not ___________________________
|
___________________________ TestFailing.test_not ___________________________
|
||||||
|
|
||||||
self = <failure_demo.TestFailing object at 0x2e4d390>
|
self = <failure_demo.TestFailing object at 0x1dbc250>
|
||||||
|
|
||||||
def test_not(self):
|
def test_not(self):
|
||||||
def f():
|
def f():
|
||||||
return 42
|
return 42
|
||||||
> assert not f()
|
> assert not f()
|
||||||
E assert not 42
|
E assert not 42
|
||||||
E + where 42 = <function f at 0x2d36cf8>()
|
E + where 42 = <function f at 0x1d45410>()
|
||||||
|
|
||||||
failure_demo.py:38: AssertionError
|
failure_demo.py:38: AssertionError
|
||||||
_________________ TestSpecialisedExplanations.test_eq_text _________________
|
_________________ TestSpecialisedExplanations.test_eq_text _________________
|
||||||
|
|
||||||
self = <failure_demo.TestSpecialisedExplanations object at 0x2e4db10>
|
self = <failure_demo.TestSpecialisedExplanations object at 0x1dbc290>
|
||||||
|
|
||||||
def test_eq_text(self):
|
def test_eq_text(self):
|
||||||
> assert 'spam' == 'eggs'
|
> assert 'spam' == 'eggs'
|
||||||
|
@ -89,7 +89,7 @@ get on the terminal - we are working on that):
|
||||||
failure_demo.py:42: AssertionError
|
failure_demo.py:42: AssertionError
|
||||||
_____________ TestSpecialisedExplanations.test_eq_similar_text _____________
|
_____________ TestSpecialisedExplanations.test_eq_similar_text _____________
|
||||||
|
|
||||||
self = <failure_demo.TestSpecialisedExplanations object at 0x2e6cbd0>
|
self = <failure_demo.TestSpecialisedExplanations object at 0x1db9ed0>
|
||||||
|
|
||||||
def test_eq_similar_text(self):
|
def test_eq_similar_text(self):
|
||||||
> assert 'foo 1 bar' == 'foo 2 bar'
|
> assert 'foo 1 bar' == 'foo 2 bar'
|
||||||
|
@ -102,7 +102,7 @@ get on the terminal - we are working on that):
|
||||||
failure_demo.py:45: AssertionError
|
failure_demo.py:45: AssertionError
|
||||||
____________ TestSpecialisedExplanations.test_eq_multiline_text ____________
|
____________ TestSpecialisedExplanations.test_eq_multiline_text ____________
|
||||||
|
|
||||||
self = <failure_demo.TestSpecialisedExplanations object at 0x2e6cdd0>
|
self = <failure_demo.TestSpecialisedExplanations object at 0x1db9d90>
|
||||||
|
|
||||||
def test_eq_multiline_text(self):
|
def test_eq_multiline_text(self):
|
||||||
> assert 'foo\nspam\nbar' == 'foo\neggs\nbar'
|
> assert 'foo\nspam\nbar' == 'foo\neggs\nbar'
|
||||||
|
@ -115,7 +115,7 @@ get on the terminal - we are working on that):
|
||||||
failure_demo.py:48: AssertionError
|
failure_demo.py:48: AssertionError
|
||||||
______________ TestSpecialisedExplanations.test_eq_long_text _______________
|
______________ TestSpecialisedExplanations.test_eq_long_text _______________
|
||||||
|
|
||||||
self = <failure_demo.TestSpecialisedExplanations object at 0x2e6cad0>
|
self = <failure_demo.TestSpecialisedExplanations object at 0x1db43d0>
|
||||||
|
|
||||||
def test_eq_long_text(self):
|
def test_eq_long_text(self):
|
||||||
a = '1'*100 + 'a' + '2'*100
|
a = '1'*100 + 'a' + '2'*100
|
||||||
|
@ -132,7 +132,7 @@ get on the terminal - we are working on that):
|
||||||
failure_demo.py:53: AssertionError
|
failure_demo.py:53: AssertionError
|
||||||
_________ TestSpecialisedExplanations.test_eq_long_text_multiline __________
|
_________ TestSpecialisedExplanations.test_eq_long_text_multiline __________
|
||||||
|
|
||||||
self = <failure_demo.TestSpecialisedExplanations object at 0x2e6cb50>
|
self = <failure_demo.TestSpecialisedExplanations object at 0x1db4390>
|
||||||
|
|
||||||
def test_eq_long_text_multiline(self):
|
def test_eq_long_text_multiline(self):
|
||||||
a = '1\n'*100 + 'a' + '2\n'*100
|
a = '1\n'*100 + 'a' + '2\n'*100
|
||||||
|
@ -156,7 +156,7 @@ get on the terminal - we are working on that):
|
||||||
failure_demo.py:58: AssertionError
|
failure_demo.py:58: AssertionError
|
||||||
_________________ TestSpecialisedExplanations.test_eq_list _________________
|
_________________ TestSpecialisedExplanations.test_eq_list _________________
|
||||||
|
|
||||||
self = <failure_demo.TestSpecialisedExplanations object at 0x2dca1d0>
|
self = <failure_demo.TestSpecialisedExplanations object at 0x1db4dd0>
|
||||||
|
|
||||||
def test_eq_list(self):
|
def test_eq_list(self):
|
||||||
> assert [0, 1, 2] == [0, 1, 3]
|
> assert [0, 1, 2] == [0, 1, 3]
|
||||||
|
@ -166,7 +166,7 @@ get on the terminal - we are working on that):
|
||||||
failure_demo.py:61: AssertionError
|
failure_demo.py:61: AssertionError
|
||||||
______________ TestSpecialisedExplanations.test_eq_list_long _______________
|
______________ TestSpecialisedExplanations.test_eq_list_long _______________
|
||||||
|
|
||||||
self = <failure_demo.TestSpecialisedExplanations object at 0x2dcad10>
|
self = <failure_demo.TestSpecialisedExplanations object at 0x1db4950>
|
||||||
|
|
||||||
def test_eq_list_long(self):
|
def test_eq_list_long(self):
|
||||||
a = [0]*100 + [1] + [3]*100
|
a = [0]*100 + [1] + [3]*100
|
||||||
|
@ -178,7 +178,7 @@ get on the terminal - we are working on that):
|
||||||
failure_demo.py:66: AssertionError
|
failure_demo.py:66: AssertionError
|
||||||
_________________ TestSpecialisedExplanations.test_eq_dict _________________
|
_________________ TestSpecialisedExplanations.test_eq_dict _________________
|
||||||
|
|
||||||
self = <failure_demo.TestSpecialisedExplanations object at 0x2dca3d0>
|
self = <failure_demo.TestSpecialisedExplanations object at 0x1db4510>
|
||||||
|
|
||||||
def test_eq_dict(self):
|
def test_eq_dict(self):
|
||||||
> assert {'a': 0, 'b': 1} == {'a': 0, 'b': 2}
|
> assert {'a': 0, 'b': 1} == {'a': 0, 'b': 2}
|
||||||
|
@ -191,7 +191,7 @@ get on the terminal - we are working on that):
|
||||||
failure_demo.py:69: AssertionError
|
failure_demo.py:69: AssertionError
|
||||||
_________________ TestSpecialisedExplanations.test_eq_set __________________
|
_________________ TestSpecialisedExplanations.test_eq_set __________________
|
||||||
|
|
||||||
self = <failure_demo.TestSpecialisedExplanations object at 0x2dca710>
|
self = <failure_demo.TestSpecialisedExplanations object at 0x1db40d0>
|
||||||
|
|
||||||
def test_eq_set(self):
|
def test_eq_set(self):
|
||||||
> assert set([0, 10, 11, 12]) == set([0, 20, 21])
|
> assert set([0, 10, 11, 12]) == set([0, 20, 21])
|
||||||
|
@ -207,7 +207,7 @@ get on the terminal - we are working on that):
|
||||||
failure_demo.py:72: AssertionError
|
failure_demo.py:72: AssertionError
|
||||||
_____________ TestSpecialisedExplanations.test_eq_longer_list ______________
|
_____________ TestSpecialisedExplanations.test_eq_longer_list ______________
|
||||||
|
|
||||||
self = <failure_demo.TestSpecialisedExplanations object at 0x2dca490>
|
self = <failure_demo.TestSpecialisedExplanations object at 0x1db4150>
|
||||||
|
|
||||||
def test_eq_longer_list(self):
|
def test_eq_longer_list(self):
|
||||||
> assert [1,2] == [1,2,3]
|
> assert [1,2] == [1,2,3]
|
||||||
|
@ -217,7 +217,7 @@ get on the terminal - we are working on that):
|
||||||
failure_demo.py:75: AssertionError
|
failure_demo.py:75: AssertionError
|
||||||
_________________ TestSpecialisedExplanations.test_in_list _________________
|
_________________ TestSpecialisedExplanations.test_in_list _________________
|
||||||
|
|
||||||
self = <failure_demo.TestSpecialisedExplanations object at 0x2dca510>
|
self = <failure_demo.TestSpecialisedExplanations object at 0x1da5590>
|
||||||
|
|
||||||
def test_in_list(self):
|
def test_in_list(self):
|
||||||
> assert 1 in [0, 2, 3, 4, 5]
|
> assert 1 in [0, 2, 3, 4, 5]
|
||||||
|
@ -226,7 +226,7 @@ get on the terminal - we are working on that):
|
||||||
failure_demo.py:78: AssertionError
|
failure_demo.py:78: AssertionError
|
||||||
__________ TestSpecialisedExplanations.test_not_in_text_multiline __________
|
__________ TestSpecialisedExplanations.test_not_in_text_multiline __________
|
||||||
|
|
||||||
self = <failure_demo.TestSpecialisedExplanations object at 0x2db86d0>
|
self = <failure_demo.TestSpecialisedExplanations object at 0x1da5f50>
|
||||||
|
|
||||||
def test_not_in_text_multiline(self):
|
def test_not_in_text_multiline(self):
|
||||||
text = 'some multiline\ntext\nwhich\nincludes foo\nand a\ntail'
|
text = 'some multiline\ntext\nwhich\nincludes foo\nand a\ntail'
|
||||||
|
@ -244,7 +244,7 @@ get on the terminal - we are working on that):
|
||||||
failure_demo.py:82: AssertionError
|
failure_demo.py:82: AssertionError
|
||||||
___________ TestSpecialisedExplanations.test_not_in_text_single ____________
|
___________ TestSpecialisedExplanations.test_not_in_text_single ____________
|
||||||
|
|
||||||
self = <failure_demo.TestSpecialisedExplanations object at 0x2db8450>
|
self = <failure_demo.TestSpecialisedExplanations object at 0x1da5cd0>
|
||||||
|
|
||||||
def test_not_in_text_single(self):
|
def test_not_in_text_single(self):
|
||||||
text = 'single foo line'
|
text = 'single foo line'
|
||||||
|
@ -257,7 +257,7 @@ get on the terminal - we are working on that):
|
||||||
failure_demo.py:86: AssertionError
|
failure_demo.py:86: AssertionError
|
||||||
_________ TestSpecialisedExplanations.test_not_in_text_single_long _________
|
_________ TestSpecialisedExplanations.test_not_in_text_single_long _________
|
||||||
|
|
||||||
self = <failure_demo.TestSpecialisedExplanations object at 0x2db8910>
|
self = <failure_demo.TestSpecialisedExplanations object at 0x1da5890>
|
||||||
|
|
||||||
def test_not_in_text_single_long(self):
|
def test_not_in_text_single_long(self):
|
||||||
text = 'head ' * 50 + 'foo ' + 'tail ' * 20
|
text = 'head ' * 50 + 'foo ' + 'tail ' * 20
|
||||||
|
@ -270,7 +270,7 @@ get on the terminal - we are working on that):
|
||||||
failure_demo.py:90: AssertionError
|
failure_demo.py:90: AssertionError
|
||||||
______ TestSpecialisedExplanations.test_not_in_text_single_long_term _______
|
______ TestSpecialisedExplanations.test_not_in_text_single_long_term _______
|
||||||
|
|
||||||
self = <failure_demo.TestSpecialisedExplanations object at 0x2db88d0>
|
self = <failure_demo.TestSpecialisedExplanations object at 0x1da53d0>
|
||||||
|
|
||||||
def test_not_in_text_single_long_term(self):
|
def test_not_in_text_single_long_term(self):
|
||||||
text = 'head ' * 50 + 'f'*70 + 'tail ' * 20
|
text = 'head ' * 50 + 'f'*70 + 'tail ' * 20
|
||||||
|
@ -289,7 +289,7 @@ get on the terminal - we are working on that):
|
||||||
i = Foo()
|
i = Foo()
|
||||||
> assert i.b == 2
|
> assert i.b == 2
|
||||||
E assert 1 == 2
|
E assert 1 == 2
|
||||||
E + where 1 = <failure_demo.Foo object at 0x2db8d90>.b
|
E + where 1 = <failure_demo.Foo object at 0x1da5750>.b
|
||||||
|
|
||||||
failure_demo.py:101: AssertionError
|
failure_demo.py:101: AssertionError
|
||||||
_________________________ test_attribute_instance __________________________
|
_________________________ test_attribute_instance __________________________
|
||||||
|
@ -299,8 +299,8 @@ get on the terminal - we are working on that):
|
||||||
b = 1
|
b = 1
|
||||||
> assert Foo().b == 2
|
> assert Foo().b == 2
|
||||||
E assert 1 == 2
|
E assert 1 == 2
|
||||||
E + where 1 = <failure_demo.Foo object at 0x2db8f50>.b
|
E + where 1 = <failure_demo.Foo object at 0x1da5710>.b
|
||||||
E + where <failure_demo.Foo object at 0x2db8f50> = <class 'failure_demo.Foo'>()
|
E + where <failure_demo.Foo object at 0x1da5710> = <class 'failure_demo.Foo'>()
|
||||||
|
|
||||||
failure_demo.py:107: AssertionError
|
failure_demo.py:107: AssertionError
|
||||||
__________________________ test_attribute_failure __________________________
|
__________________________ test_attribute_failure __________________________
|
||||||
|
@ -316,7 +316,7 @@ get on the terminal - we are working on that):
|
||||||
failure_demo.py:116:
|
failure_demo.py:116:
|
||||||
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
|
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
|
||||||
|
|
||||||
self = <failure_demo.Foo object at 0x2db8dd0>
|
self = <failure_demo.Foo object at 0x1da5090>
|
||||||
|
|
||||||
def _get_b(self):
|
def _get_b(self):
|
||||||
> raise Exception('Failed to get attrib')
|
> raise Exception('Failed to get attrib')
|
||||||
|
@ -332,15 +332,15 @@ get on the terminal - we are working on that):
|
||||||
b = 2
|
b = 2
|
||||||
> assert Foo().b == Bar().b
|
> assert Foo().b == Bar().b
|
||||||
E assert 1 == 2
|
E assert 1 == 2
|
||||||
E + where 1 = <failure_demo.Foo object at 0x2db8c10>.b
|
E + where 1 = <failure_demo.Foo object at 0x1da5b50>.b
|
||||||
E + where <failure_demo.Foo object at 0x2db8c10> = <class 'failure_demo.Foo'>()
|
E + where <failure_demo.Foo object at 0x1da5b50> = <class 'failure_demo.Foo'>()
|
||||||
E + and 2 = <failure_demo.Bar object at 0x2db8b90>.b
|
E + and 2 = <failure_demo.Bar object at 0x1d51ad0>.b
|
||||||
E + where <failure_demo.Bar object at 0x2db8b90> = <class 'failure_demo.Bar'>()
|
E + where <failure_demo.Bar object at 0x1d51ad0> = <class 'failure_demo.Bar'>()
|
||||||
|
|
||||||
failure_demo.py:124: AssertionError
|
failure_demo.py:124: AssertionError
|
||||||
__________________________ TestRaises.test_raises __________________________
|
__________________________ TestRaises.test_raises __________________________
|
||||||
|
|
||||||
self = <failure_demo.TestRaises instance at 0x2d93cf8>
|
self = <failure_demo.TestRaises instance at 0x1dc5d40>
|
||||||
|
|
||||||
def test_raises(self):
|
def test_raises(self):
|
||||||
s = 'qwe'
|
s = 'qwe'
|
||||||
|
@ -352,10 +352,10 @@ get on the terminal - we are working on that):
|
||||||
> int(s)
|
> int(s)
|
||||||
E ValueError: invalid literal for int() with base 10: 'qwe'
|
E ValueError: invalid literal for int() with base 10: 'qwe'
|
||||||
|
|
||||||
<0-codegen /home/hpk/p/pytest/_pytest/python.py:978>:1: ValueError
|
<0-codegen /home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/python.py:838>:1: ValueError
|
||||||
______________________ TestRaises.test_raises_doesnt _______________________
|
______________________ TestRaises.test_raises_doesnt _______________________
|
||||||
|
|
||||||
self = <failure_demo.TestRaises instance at 0x2e69c20>
|
self = <failure_demo.TestRaises instance at 0x1dc7b90>
|
||||||
|
|
||||||
def test_raises_doesnt(self):
|
def test_raises_doesnt(self):
|
||||||
> raises(IOError, "int('3')")
|
> raises(IOError, "int('3')")
|
||||||
|
@ -364,7 +364,7 @@ get on the terminal - we are working on that):
|
||||||
failure_demo.py:136: Failed
|
failure_demo.py:136: Failed
|
||||||
__________________________ TestRaises.test_raise ___________________________
|
__________________________ TestRaises.test_raise ___________________________
|
||||||
|
|
||||||
self = <failure_demo.TestRaises instance at 0x2d96098>
|
self = <failure_demo.TestRaises instance at 0x1dc0a28>
|
||||||
|
|
||||||
def test_raise(self):
|
def test_raise(self):
|
||||||
> raise ValueError("demo error")
|
> raise ValueError("demo error")
|
||||||
|
@ -373,7 +373,7 @@ get on the terminal - we are working on that):
|
||||||
failure_demo.py:139: ValueError
|
failure_demo.py:139: ValueError
|
||||||
________________________ TestRaises.test_tupleerror ________________________
|
________________________ TestRaises.test_tupleerror ________________________
|
||||||
|
|
||||||
self = <failure_demo.TestRaises instance at 0x2d96e60>
|
self = <failure_demo.TestRaises instance at 0x1dc37a0>
|
||||||
|
|
||||||
def test_tupleerror(self):
|
def test_tupleerror(self):
|
||||||
> a,b = [1]
|
> a,b = [1]
|
||||||
|
@ -382,7 +382,7 @@ get on the terminal - we are working on that):
|
||||||
failure_demo.py:142: ValueError
|
failure_demo.py:142: ValueError
|
||||||
______ TestRaises.test_reinterpret_fails_with_print_for_the_fun_of_it ______
|
______ TestRaises.test_reinterpret_fails_with_print_for_the_fun_of_it ______
|
||||||
|
|
||||||
self = <failure_demo.TestRaises instance at 0x2d99b90>
|
self = <failure_demo.TestRaises instance at 0x1dba518>
|
||||||
|
|
||||||
def test_reinterpret_fails_with_print_for_the_fun_of_it(self):
|
def test_reinterpret_fails_with_print_for_the_fun_of_it(self):
|
||||||
l = [1,2,3]
|
l = [1,2,3]
|
||||||
|
@ -395,7 +395,7 @@ get on the terminal - we are working on that):
|
||||||
l is [1, 2, 3]
|
l is [1, 2, 3]
|
||||||
________________________ TestRaises.test_some_error ________________________
|
________________________ TestRaises.test_some_error ________________________
|
||||||
|
|
||||||
self = <failure_demo.TestRaises instance at 0x2d9b998>
|
self = <failure_demo.TestRaises instance at 0x1dbf320>
|
||||||
|
|
||||||
def test_some_error(self):
|
def test_some_error(self):
|
||||||
> if namenotexi:
|
> if namenotexi:
|
||||||
|
@ -420,10 +420,10 @@ get on the terminal - we are working on that):
|
||||||
> assert 1 == 0
|
> assert 1 == 0
|
||||||
E assert 1 == 0
|
E assert 1 == 0
|
||||||
|
|
||||||
<2-codegen 'abc-123' /home/hpk/p/pytest/doc/example/assertion/failure_demo.py:162>:2: AssertionError
|
<2-codegen 'abc-123' /home/hpk/p/pytest/doc/en/example/assertion/failure_demo.py:162>:2: AssertionError
|
||||||
____________________ TestMoreErrors.test_complex_error _____________________
|
____________________ TestMoreErrors.test_complex_error _____________________
|
||||||
|
|
||||||
self = <failure_demo.TestMoreErrors instance at 0x2e5b6c8>
|
self = <failure_demo.TestMoreErrors instance at 0x1dc7f38>
|
||||||
|
|
||||||
def test_complex_error(self):
|
def test_complex_error(self):
|
||||||
def f():
|
def f():
|
||||||
|
@ -452,7 +452,7 @@ get on the terminal - we are working on that):
|
||||||
failure_demo.py:5: AssertionError
|
failure_demo.py:5: AssertionError
|
||||||
___________________ TestMoreErrors.test_z1_unpack_error ____________________
|
___________________ TestMoreErrors.test_z1_unpack_error ____________________
|
||||||
|
|
||||||
self = <failure_demo.TestMoreErrors instance at 0x2d9a998>
|
self = <failure_demo.TestMoreErrors instance at 0x1dbe320>
|
||||||
|
|
||||||
def test_z1_unpack_error(self):
|
def test_z1_unpack_error(self):
|
||||||
l = []
|
l = []
|
||||||
|
@ -462,7 +462,7 @@ get on the terminal - we are working on that):
|
||||||
failure_demo.py:179: ValueError
|
failure_demo.py:179: ValueError
|
||||||
____________________ TestMoreErrors.test_z2_type_error _____________________
|
____________________ TestMoreErrors.test_z2_type_error _____________________
|
||||||
|
|
||||||
self = <failure_demo.TestMoreErrors instance at 0x2dc87a0>
|
self = <failure_demo.TestMoreErrors instance at 0x1db6170>
|
||||||
|
|
||||||
def test_z2_type_error(self):
|
def test_z2_type_error(self):
|
||||||
l = 3
|
l = 3
|
||||||
|
@ -472,19 +472,19 @@ get on the terminal - we are working on that):
|
||||||
failure_demo.py:183: TypeError
|
failure_demo.py:183: TypeError
|
||||||
______________________ TestMoreErrors.test_startswith ______________________
|
______________________ TestMoreErrors.test_startswith ______________________
|
||||||
|
|
||||||
self = <failure_demo.TestMoreErrors instance at 0x2dc55a8>
|
self = <failure_demo.TestMoreErrors instance at 0x1db6f38>
|
||||||
|
|
||||||
def test_startswith(self):
|
def test_startswith(self):
|
||||||
s = "123"
|
s = "123"
|
||||||
g = "456"
|
g = "456"
|
||||||
> assert s.startswith(g)
|
> assert s.startswith(g)
|
||||||
E assert <built-in method startswith of str object at 0x2daf3f0>('456')
|
E assert <built-in method startswith of str object at 0x1dbdad0>('456')
|
||||||
E + where <built-in method startswith of str object at 0x2daf3f0> = '123'.startswith
|
E + where <built-in method startswith of str object at 0x1dbdad0> = '123'.startswith
|
||||||
|
|
||||||
failure_demo.py:188: AssertionError
|
failure_demo.py:188: AssertionError
|
||||||
__________________ TestMoreErrors.test_startswith_nested ___________________
|
__________________ TestMoreErrors.test_startswith_nested ___________________
|
||||||
|
|
||||||
self = <failure_demo.TestMoreErrors instance at 0x2dc8518>
|
self = <failure_demo.TestMoreErrors instance at 0x1dbed40>
|
||||||
|
|
||||||
def test_startswith_nested(self):
|
def test_startswith_nested(self):
|
||||||
def f():
|
def f():
|
||||||
|
@ -492,15 +492,15 @@ get on the terminal - we are working on that):
|
||||||
def g():
|
def g():
|
||||||
return "456"
|
return "456"
|
||||||
> assert f().startswith(g())
|
> assert f().startswith(g())
|
||||||
E assert <built-in method startswith of str object at 0x2daf3f0>('456')
|
E assert <built-in method startswith of str object at 0x1dbdad0>('456')
|
||||||
E + where <built-in method startswith of str object at 0x2daf3f0> = '123'.startswith
|
E + where <built-in method startswith of str object at 0x1dbdad0> = '123'.startswith
|
||||||
E + where '123' = <function f at 0x2e50aa0>()
|
E + where '123' = <function f at 0x1ddf488>()
|
||||||
E + and '456' = <function g at 0x2e52cf8>()
|
E + and '456' = <function g at 0x1ddf848>()
|
||||||
|
|
||||||
failure_demo.py:195: AssertionError
|
failure_demo.py:195: AssertionError
|
||||||
_____________________ TestMoreErrors.test_global_func ______________________
|
_____________________ TestMoreErrors.test_global_func ______________________
|
||||||
|
|
||||||
self = <failure_demo.TestMoreErrors instance at 0x2e69098>
|
self = <failure_demo.TestMoreErrors instance at 0x1dc79e0>
|
||||||
|
|
||||||
def test_global_func(self):
|
def test_global_func(self):
|
||||||
> assert isinstance(globf(42), float)
|
> assert isinstance(globf(42), float)
|
||||||
|
@ -510,18 +510,18 @@ get on the terminal - we are working on that):
|
||||||
failure_demo.py:198: AssertionError
|
failure_demo.py:198: AssertionError
|
||||||
_______________________ TestMoreErrors.test_instance _______________________
|
_______________________ TestMoreErrors.test_instance _______________________
|
||||||
|
|
||||||
self = <failure_demo.TestMoreErrors instance at 0x2d96fc8>
|
self = <failure_demo.TestMoreErrors instance at 0x1dc3098>
|
||||||
|
|
||||||
def test_instance(self):
|
def test_instance(self):
|
||||||
self.x = 6*7
|
self.x = 6*7
|
||||||
> assert self.x != 42
|
> assert self.x != 42
|
||||||
E assert 42 != 42
|
E assert 42 != 42
|
||||||
E + where 42 = <failure_demo.TestMoreErrors instance at 0x2d96fc8>.x
|
E + where 42 = <failure_demo.TestMoreErrors instance at 0x1dc3098>.x
|
||||||
|
|
||||||
failure_demo.py:202: AssertionError
|
failure_demo.py:202: AssertionError
|
||||||
_______________________ TestMoreErrors.test_compare ________________________
|
_______________________ TestMoreErrors.test_compare ________________________
|
||||||
|
|
||||||
self = <failure_demo.TestMoreErrors instance at 0x2d9e170>
|
self = <failure_demo.TestMoreErrors instance at 0x1db0ab8>
|
||||||
|
|
||||||
def test_compare(self):
|
def test_compare(self):
|
||||||
> assert globf(10) < 5
|
> assert globf(10) < 5
|
||||||
|
@ -531,7 +531,7 @@ get on the terminal - we are working on that):
|
||||||
failure_demo.py:205: AssertionError
|
failure_demo.py:205: AssertionError
|
||||||
_____________________ TestMoreErrors.test_try_finally ______________________
|
_____________________ TestMoreErrors.test_try_finally ______________________
|
||||||
|
|
||||||
self = <failure_demo.TestMoreErrors instance at 0x2d9ef80>
|
self = <failure_demo.TestMoreErrors instance at 0x1db38c0>
|
||||||
|
|
||||||
def test_try_finally(self):
|
def test_try_finally(self):
|
||||||
x = 1
|
x = 1
|
||||||
|
@ -540,4 +540,4 @@ get on the terminal - we are working on that):
|
||||||
E assert 1 == 0
|
E assert 1 == 0
|
||||||
|
|
||||||
failure_demo.py:210: AssertionError
|
failure_demo.py:210: AssertionError
|
||||||
======================== 39 failed in 0.17 seconds =========================
|
======================== 39 failed in 0.15 seconds =========================
|
||||||
|
|
|
@ -22,20 +22,22 @@ Here is a basic pattern how to achieve this::
|
||||||
|
|
||||||
|
|
||||||
For this to work we need to add a command line option and
|
For this to work we need to add a command line option and
|
||||||
provide the ``cmdopt`` through a :ref:`function argument <funcarg>` factory::
|
provide the ``cmdopt`` through a :ref:`fixture function <fixture function>`::
|
||||||
|
|
||||||
# content of conftest.py
|
# content of conftest.py
|
||||||
|
import pytest
|
||||||
|
|
||||||
def pytest_addoption(parser):
|
def pytest_addoption(parser):
|
||||||
parser.addoption("--cmdopt", action="store", default="type1",
|
parser.addoption("--cmdopt", action="store", default="type1",
|
||||||
help="my option: type1 or type2")
|
help="my option: type1 or type2")
|
||||||
|
|
||||||
def pytest_funcarg__cmdopt(request):
|
@pytest.fixture
|
||||||
|
def cmdopt(request):
|
||||||
return request.config.option.cmdopt
|
return request.config.option.cmdopt
|
||||||
|
|
||||||
Let's run this without supplying our new command line option::
|
Let's run this without supplying our new option::
|
||||||
|
|
||||||
$ py.test -q test_sample.py
|
$ py.test -q test_sample.py
|
||||||
collecting ... collected 1 items
|
|
||||||
F
|
F
|
||||||
================================= FAILURES =================================
|
================================= FAILURES =================================
|
||||||
_______________________________ test_answer ________________________________
|
_______________________________ test_answer ________________________________
|
||||||
|
@ -53,12 +55,10 @@ Let's run this without supplying our new command line option::
|
||||||
test_sample.py:6: AssertionError
|
test_sample.py:6: AssertionError
|
||||||
----------------------------- Captured stdout ------------------------------
|
----------------------------- Captured stdout ------------------------------
|
||||||
first
|
first
|
||||||
1 failed in 0.01 seconds
|
|
||||||
|
|
||||||
And now with supplying a command line option::
|
And now with supplying a command line option::
|
||||||
|
|
||||||
$ py.test -q --cmdopt=type2
|
$ py.test -q --cmdopt=type2
|
||||||
collecting ... collected 1 items
|
|
||||||
F
|
F
|
||||||
================================= FAILURES =================================
|
================================= FAILURES =================================
|
||||||
_______________________________ test_answer ________________________________
|
_______________________________ test_answer ________________________________
|
||||||
|
@ -76,14 +76,11 @@ And now with supplying a command line option::
|
||||||
test_sample.py:6: AssertionError
|
test_sample.py:6: AssertionError
|
||||||
----------------------------- Captured stdout ------------------------------
|
----------------------------- Captured stdout ------------------------------
|
||||||
second
|
second
|
||||||
1 failed in 0.01 seconds
|
|
||||||
|
|
||||||
Ok, this completes the basic pattern. However, one often rather
|
|
||||||
wants to process command line options outside of the test and
|
|
||||||
rather pass in different or more complex objects. See the
|
|
||||||
next example or refer to :ref:`mysetup` for more information
|
|
||||||
on real-life examples.
|
|
||||||
|
|
||||||
|
You can see that the command line option arrived in our test. This
|
||||||
|
completes the basic pattern. However, one often rather wants to process
|
||||||
|
command line options outside of the test and rather pass in different or
|
||||||
|
more complex objects.
|
||||||
|
|
||||||
Dynamically adding command line options
|
Dynamically adding command line options
|
||||||
--------------------------------------------------------------
|
--------------------------------------------------------------
|
||||||
|
@ -109,13 +106,10 @@ directory with the above conftest.py::
|
||||||
|
|
||||||
$ py.test
|
$ py.test
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux2 -- Python 2.7.1 -- pytest-2.2.4
|
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19
|
||||||
gw0 I / gw1 I / gw2 I / gw3 I
|
collected 0 items
|
||||||
gw0 [0] / gw1 [0] / gw2 [0] / gw3 [0]
|
|
||||||
|
|
||||||
scheduling tests via LoadScheduling
|
============================= in 0.00 seconds =============================
|
||||||
|
|
||||||
============================= in 0.52 seconds =============================
|
|
||||||
|
|
||||||
.. _`excontrolskip`:
|
.. _`excontrolskip`:
|
||||||
|
|
||||||
|
@ -156,12 +150,12 @@ and when running it will see a skipped "slow" test::
|
||||||
|
|
||||||
$ py.test -rs # "-rs" means report details on the little 's'
|
$ py.test -rs # "-rs" means report details on the little 's'
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux2 -- Python 2.7.1 -- pytest-2.2.4
|
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19
|
||||||
collecting ... collected 2 items
|
collected 2 items
|
||||||
|
|
||||||
test_module.py .s
|
test_module.py .s
|
||||||
========================= short test summary info ==========================
|
========================= short test summary info ==========================
|
||||||
SKIP [1] /tmp/doc-exec-225/conftest.py:9: need --runslow option to run
|
SKIP [1] /tmp/doc-exec-264/conftest.py:9: need --runslow option to run
|
||||||
|
|
||||||
=================== 1 passed, 1 skipped in 0.01 seconds ====================
|
=================== 1 passed, 1 skipped in 0.01 seconds ====================
|
||||||
|
|
||||||
|
@ -169,8 +163,8 @@ Or run it including the ``slow`` marked test::
|
||||||
|
|
||||||
$ py.test --runslow
|
$ py.test --runslow
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux2 -- Python 2.7.1 -- pytest-2.2.4
|
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19
|
||||||
collecting ... collected 2 items
|
collected 2 items
|
||||||
|
|
||||||
test_module.py ..
|
test_module.py ..
|
||||||
|
|
||||||
|
@ -203,7 +197,6 @@ unless the ``--fulltrace`` command line option is specified.
|
||||||
Let's run our little function::
|
Let's run our little function::
|
||||||
|
|
||||||
$ py.test -q test_checkconfig.py
|
$ py.test -q test_checkconfig.py
|
||||||
collecting ... collected 1 items
|
|
||||||
F
|
F
|
||||||
================================= FAILURES =================================
|
================================= FAILURES =================================
|
||||||
______________________________ test_something ______________________________
|
______________________________ test_something ______________________________
|
||||||
|
@ -213,7 +206,6 @@ Let's run our little function::
|
||||||
E Failed: not configured: 42
|
E Failed: not configured: 42
|
||||||
|
|
||||||
test_checkconfig.py:8: Failed
|
test_checkconfig.py:8: Failed
|
||||||
1 failed in 0.01 seconds
|
|
||||||
|
|
||||||
Detect if running from within a py.test run
|
Detect if running from within a py.test run
|
||||||
--------------------------------------------------------------
|
--------------------------------------------------------------
|
||||||
|
@ -261,9 +253,9 @@ which will add the string to the test header accordingly::
|
||||||
|
|
||||||
$ py.test
|
$ py.test
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux2 -- Python 2.7.1 -- pytest-2.2.4
|
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19
|
||||||
project deps: mylib-1.1
|
project deps: mylib-1.1
|
||||||
collecting ... collected 0 items
|
collected 0 items
|
||||||
|
|
||||||
============================= in 0.00 seconds =============================
|
============================= in 0.00 seconds =============================
|
||||||
|
|
||||||
|
@ -284,7 +276,7 @@ which will add info only when run with "--v"::
|
||||||
|
|
||||||
$ py.test -v
|
$ py.test -v
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux2 -- Python 2.7.1 -- pytest-2.2.4 -- /home/hpk/venv/0/bin/python
|
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 -- /home/hpk/p/pytest/.tox/regen/bin/python
|
||||||
info1: did you know that ...
|
info1: did you know that ...
|
||||||
did you?
|
did you?
|
||||||
collecting ... collected 0 items
|
collecting ... collected 0 items
|
||||||
|
@ -295,8 +287,8 @@ and nothing when run plainly::
|
||||||
|
|
||||||
$ py.test
|
$ py.test
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux2 -- Python 2.7.1 -- pytest-2.2.4
|
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19
|
||||||
collecting ... collected 0 items
|
collected 0 items
|
||||||
|
|
||||||
============================= in 0.00 seconds =============================
|
============================= in 0.00 seconds =============================
|
||||||
|
|
||||||
|
@ -327,13 +319,13 @@ Now we can profile which test functions execute the slowest::
|
||||||
|
|
||||||
$ py.test --durations=3
|
$ py.test --durations=3
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux2 -- Python 2.7.1 -- pytest-2.2.4
|
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19
|
||||||
collecting ... collected 3 items
|
collected 3 items
|
||||||
|
|
||||||
test_some_are_slow.py ...
|
test_some_are_slow.py ...
|
||||||
|
|
||||||
========================= slowest 3 test durations =========================
|
========================= slowest 3 test durations =========================
|
||||||
0.20s call test_some_are_slow.py::test_funcslow2
|
0.20s call test_some_are_slow.py::test_funcslow2
|
||||||
0.10s call test_some_are_slow.py::test_funcslow1
|
0.10s call test_some_are_slow.py::test_funcslow1
|
||||||
0.00s setup test_some_are_slow.py::test_funcslow2
|
0.00s call test_some_are_slow.py::test_funcfast
|
||||||
========================= 3 passed in 0.31 seconds =========================
|
========================= 3 passed in 0.31 seconds =========================
|
||||||
|
|
|
@ -1,17 +1,12 @@
|
||||||
.. _xunitsetup:
|
|
||||||
.. _setup:
|
|
||||||
.. _fixture:
|
.. _fixture:
|
||||||
|
.. _fixtures:
|
||||||
.. _`fixture functions`:
|
.. _`fixture functions`:
|
||||||
.. _`@pytest.fixture`:
|
|
||||||
|
|
||||||
pytest fixtures: modular, re-useable, flexible
|
pytest fixtures: modular, explicit, scalable
|
||||||
========================================================
|
========================================================
|
||||||
|
|
||||||
.. versionadded:: 2.0, 2.3
|
.. versionadded:: 2.0, 2.3
|
||||||
|
|
||||||
.. _`funcargs`: funcargs.html
|
|
||||||
.. _`test parametrization`: funcargs.html#parametrizing-tests
|
|
||||||
.. _`unittest plugin`: plugin/unittest.html
|
|
||||||
.. _`xUnit`: http://en.wikipedia.org/wiki/XUnit
|
.. _`xUnit`: http://en.wikipedia.org/wiki/XUnit
|
||||||
.. _`general purpose of test fixtures`: http://en.wikipedia.org/wiki/Test_fixture#Software
|
.. _`general purpose of test fixtures`: http://en.wikipedia.org/wiki/Test_fixture#Software
|
||||||
.. _`django`: https://www.djangoproject.com/
|
.. _`django`: https://www.djangoproject.com/
|
||||||
|
@ -20,101 +15,59 @@ pytest fixtures: modular, re-useable, flexible
|
||||||
|
|
||||||
pytest allows to create and use test fixtures in a modular and flexible
|
pytest allows to create and use test fixtures in a modular and flexible
|
||||||
manner, offering dramatic improvements over the classic xUnit style of
|
manner, offering dramatic improvements over the classic xUnit style of
|
||||||
setup/teardown functions. The `general purpose of test fixtures`_ is to
|
setup/teardown functions. The `general purpose of test fixtures`_
|
||||||
provide a fixed baseline upon which tests can reliably and
|
is to provide a fixed baseline upon which tests can reliably
|
||||||
repeatedly execute. With pytest, fixtures have names and can be referenced
|
and repeatedly execute. With pytest, fixtures have names and can be
|
||||||
from test functions, modules, classes or whole projects. Fixtures are
|
activated by referencing them from test functions, modules, classes or
|
||||||
implemented by **fixture functions** which may return a fixture object
|
whole projects. Fixtures are implemented by *fixture functions* which
|
||||||
or put extra attributes on test classes or perform global side effects
|
have full access to the requesting test context and can use other
|
||||||
if needed. Fixtures can themselves access other fixtures, allowing a
|
fixtures, allowing a modular and flexible approach to organising
|
||||||
**structured modular approach** to organising fixtures for an
|
and parametrizing fixtures for an application. Complemented by
|
||||||
application.
|
pytest's generic :ref:`parametrize features <parametrize>`, pytest
|
||||||
|
fixtures help to write test suites that scale from simple to complex
|
||||||
|
with minimal effort.
|
||||||
|
|
||||||
**Test functions can receive fixture objects by naming them as an input
|
|
||||||
argument.** For each argument name, a matching fixture
|
.. _`funcargs`:
|
||||||
|
.. _`funcarg mechanism`:
|
||||||
|
.. _`fixture function`:
|
||||||
|
|
||||||
|
Fixtures as Function arguments
|
||||||
|
-----------------------------------------
|
||||||
|
|
||||||
|
Test functions can receive fixture objects by naming them as an input
|
||||||
|
argument. For each argument name, a matching fixture
|
||||||
function will provide a fixture object. This mechanism was already
|
function will provide a fixture object. This mechanism was already
|
||||||
introduced with pytest-2.0 and is also called the **funcarg mechanism**.
|
introduced with pytest-2.0 and is also called the *funcarg mechanism*.
|
||||||
It allows test functions to easily receive and work against specific
|
It allows test functions to easily receive and work against specific
|
||||||
pre-initialized application objects without having to care about the
|
pre-initialized application objects without having to care about the
|
||||||
details of setup/cleanup procedures. It's a prime example of
|
details of setup/cleanup procedures. It's a prime example of
|
||||||
`dependency injection`_ where fixture functions take the role of the
|
`dependency injection`_ where fixture functions take the role of the
|
||||||
*injector* and test functions are the *consumers* of fixture objects.
|
*injector* and test functions are the *consumers* of fixture objects.
|
||||||
With pytest-2.3 this mechanism has been generalized and improved as described
|
|
||||||
further in this document.
|
|
||||||
|
|
||||||
**Test classes, modules or whole projects can declare a need for
|
Let's look at a simple self-contained test module containing
|
||||||
one or more fixtures**. All required fixture functions will execute
|
a fixture and a test function using it::
|
||||||
before a test from the specifying context executes. You can use this
|
|
||||||
to make tests operate from a pre-initialized directory or with
|
|
||||||
certain environment variables or with pre-initialized applications.
|
|
||||||
For example, the Django_ project requires database
|
|
||||||
initialization to be able to import from and use its model objects.
|
|
||||||
For that, the `pytest-django`_ plugin provides fixtures which your
|
|
||||||
project can then easily depend or extend on, simply by referencing the
|
|
||||||
name of the particular fixture.
|
|
||||||
|
|
||||||
**Fixtures can be shared throughout a test session, module or class.**.
|
# content of ./test_fixturefuncarg.py
|
||||||
By means of a "scope" declaration on a fixture function, it will
|
|
||||||
only be invoked once per the specified scope. This allows to reduce the number
|
|
||||||
of expensive application object setups and thus helps to speed up test runs.
|
|
||||||
Typical examples are the setup of test databases or establishing
|
|
||||||
required subprocesses or network connections.
|
|
||||||
|
|
||||||
**Fixture functions have limited visilibity** which depends on where they
|
|
||||||
are defined. If they are defined on a test class, only its test methods
|
|
||||||
may use it. A fixture defined in a module can only be used
|
|
||||||
from that test module. A fixture defined in a conftest.py file
|
|
||||||
can only be used by the tests below the directory of that file.
|
|
||||||
Lastly, plugins can define fixtures which are available across all
|
|
||||||
projects.
|
|
||||||
|
|
||||||
**Fixture functions can interact with the requesting testcontext**. By
|
|
||||||
accepting a special ``request`` object, fixture functions can introspect
|
|
||||||
the function, class or module for which they are invoked and can
|
|
||||||
optionally register cleanup functions which are called when the last
|
|
||||||
test finished execution. A good example is `pytest-timeout`_ which
|
|
||||||
allows to limit the execution time of a test, and will read the
|
|
||||||
according parameter from a test function or from project-wide settings.
|
|
||||||
|
|
||||||
**Fixture functions can be parametrized** in which case they will be called
|
|
||||||
multiple times, each time executing the set of dependent tests, i. e. the
|
|
||||||
tests that depend on this fixture. Test functions do usually not need
|
|
||||||
to be aware of their re-running. Fixture parametrization helps to
|
|
||||||
write exhaustive functional tests for components which themselves can be
|
|
||||||
configured in multiple ways.
|
|
||||||
|
|
||||||
|
|
||||||
Basic test function with fixtures
|
|
||||||
-----------------------------------------------------------
|
|
||||||
|
|
||||||
.. versionadded:: 2.3
|
|
||||||
|
|
||||||
|
|
||||||
Let's look at a simple self-contained test module containing a module
|
|
||||||
visible fixture function and a test function using the provided fixture::
|
|
||||||
|
|
||||||
# content of ./test_simplefactory.py
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
@pytest.fixture()
|
@pytest.fixture
|
||||||
def myfuncarg():
|
def myfuncarg():
|
||||||
return 42
|
return 42
|
||||||
|
|
||||||
def test_function(myfuncarg):
|
def test_function(myfuncarg):
|
||||||
assert myfuncarg == 17
|
assert myfuncarg == 17 # will fail
|
||||||
|
|
||||||
Here, the ``test_function`` needs a very simple fixture ``myfuncarg`` which
|
Here, the ``test_function`` needs the ``myfuncarg`` fixture value. pytest
|
||||||
it wants to compare against a specific value. py.test will discover and call
|
will discover and call the ``@pytest.fixture`` marked ``myfuncarg``
|
||||||
the ``@pytest.fixture`` marked ``myfuncarg`` fixture function. Running the
|
fixture function. Running the test looks like this::
|
||||||
tests looks like this::
|
|
||||||
|
|
||||||
$ py.test test_simplefactory.py
|
$ py.test test_fixturefuncarg.py
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev18
|
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19
|
||||||
plugins: xdist, bugzilla, cache, oejskit, cli, pep8, cov, timeout
|
collected 1 items
|
||||||
collecting ... collected 1 items
|
|
||||||
|
|
||||||
test_simplefactory.py F
|
test_fixturefuncarg.py F
|
||||||
|
|
||||||
================================= FAILURES =================================
|
================================= FAILURES =================================
|
||||||
______________________________ test_function _______________________________
|
______________________________ test_function _______________________________
|
||||||
|
@ -122,17 +75,17 @@ tests looks like this::
|
||||||
myfuncarg = 42
|
myfuncarg = 42
|
||||||
|
|
||||||
def test_function(myfuncarg):
|
def test_function(myfuncarg):
|
||||||
> assert myfuncarg == 17
|
> assert myfuncarg == 17 # will fail
|
||||||
E assert 42 == 17
|
E assert 42 == 17
|
||||||
|
|
||||||
test_simplefactory.py:8: AssertionError
|
test_fixturefuncarg.py:8: AssertionError
|
||||||
========================= 1 failed in 0.01 seconds =========================
|
========================= 1 failed in 0.01 seconds =========================
|
||||||
|
|
||||||
This shows that the test function was called with a ``myfuncarg``
|
This shows that the test function was called with a ``myfuncarg``
|
||||||
argument value of ``42`` and the assert fails as expected. Here is
|
value of ``42`` and the assert fails as expected. Here is
|
||||||
how py.test comes to call the test function this way:
|
how py.test comes to call the test function this way:
|
||||||
|
|
||||||
1. py.test :ref:`finds <test discovery>` the ``test_function`` because
|
1. pytest :ref:`finds <test discovery>` the ``test_function`` because
|
||||||
of the ``test_`` prefix. The test function needs a function argument
|
of the ``test_`` prefix. The test function needs a function argument
|
||||||
named ``myfuncarg``. A matching fixture function is discovered by
|
named ``myfuncarg``. A matching fixture function is discovered by
|
||||||
looking for a fixture function named ``myfuncarg``.
|
looking for a fixture function named ``myfuncarg``.
|
||||||
|
@ -164,6 +117,12 @@ with a list of available function arguments.
|
||||||
Creating and using a session-shared fixture
|
Creating and using a session-shared fixture
|
||||||
-----------------------------------------------------------------
|
-----------------------------------------------------------------
|
||||||
|
|
||||||
|
By means of a "scope" declaration, a fixture function will
|
||||||
|
only be invoked once per the specified scope. This allows to reduce the
|
||||||
|
number of expensive application object setups and thus helps to speed up
|
||||||
|
test runs. Typical examples are the setup of test databases or
|
||||||
|
establishing required subprocesses or network connections.
|
||||||
|
|
||||||
.. regendoc:wipe
|
.. regendoc:wipe
|
||||||
|
|
||||||
Here is a simple example of a fixture function creating a shared
|
Here is a simple example of a fixture function creating a shared
|
||||||
|
@ -199,12 +158,11 @@ We deliberately insert failing ``assert 0`` statements in order to
|
||||||
inspect what is going on and can now run the tests::
|
inspect what is going on and can now run the tests::
|
||||||
|
|
||||||
$ py.test -q test_module.py
|
$ py.test -q test_module.py
|
||||||
collecting ... collected 2 items
|
|
||||||
FF
|
FF
|
||||||
================================= FAILURES =================================
|
================================= FAILURES =================================
|
||||||
________________________________ test_ehlo _________________________________
|
________________________________ test_ehlo _________________________________
|
||||||
|
|
||||||
smtp = <smtplib.SMTP instance at 0x2c64128>
|
smtp = <smtplib.SMTP instance at 0x1c51440>
|
||||||
|
|
||||||
def test_ehlo(smtp):
|
def test_ehlo(smtp):
|
||||||
response = smtp.ehlo()
|
response = smtp.ehlo()
|
||||||
|
@ -213,10 +171,10 @@ inspect what is going on and can now run the tests::
|
||||||
> assert 0 # for demo purposes
|
> assert 0 # for demo purposes
|
||||||
E assert 0
|
E assert 0
|
||||||
|
|
||||||
test_module.py:5: AssertionError
|
test_module.py:6: AssertionError
|
||||||
________________________________ test_noop _________________________________
|
________________________________ test_noop _________________________________
|
||||||
|
|
||||||
smtp = <smtplib.SMTP instance at 0x2c64128>
|
smtp = <smtplib.SMTP instance at 0x1c51440>
|
||||||
|
|
||||||
def test_noop(smtp):
|
def test_noop(smtp):
|
||||||
response = smtp.noop()
|
response = smtp.noop()
|
||||||
|
@ -224,32 +182,34 @@ inspect what is going on and can now run the tests::
|
||||||
> assert 0 # for demo purposes
|
> assert 0 # for demo purposes
|
||||||
E assert 0
|
E assert 0
|
||||||
|
|
||||||
test_module.py:10: AssertionError
|
test_module.py:11: AssertionError
|
||||||
2 failed in 0.15 seconds
|
|
||||||
|
|
||||||
you see the two ``assert 0`` failing and can also see that
|
you see the two ``assert 0`` failing and can also see that
|
||||||
the same (session-scoped) object was passed into the two test functions
|
the same (session-scoped) object was passed into the two test functions
|
||||||
because pytest shows the incoming arguments in the traceback.
|
because pytest shows the incoming arguments in the traceback.
|
||||||
|
|
||||||
|
|
||||||
Adding a finalizer to a fixture
|
Fixtures can interact with the requesting test context
|
||||||
--------------------------------------------------------
|
-------------------------------------------------------------
|
||||||
|
|
||||||
Further extending the ``smtp`` example, we now want to properly
|
By using the special :ref:`request` object, fixture functions can introspect
|
||||||
close a smtp server connection after the last test using it
|
the function, class or module for which they are invoked and can
|
||||||
has been run. We can do this by changing the fixture function
|
optionally register cleanup functions which are called when the last
|
||||||
to accept the special :ref:`request` object, representing the
|
test finished execution.
|
||||||
requesting test context. After calling the ``request.addfinalizer()``
|
|
||||||
helper pytest will make sure that the finalizer function is called
|
Further extending the previous ``smtp`` fixture example, let's try to
|
||||||
after the last test using the ``smtp`` resource has finished.
|
read the server URL from the module namespace, use module-scoping and
|
||||||
|
register a finalizer that closes the smtp connection after the last
|
||||||
|
test finished execution::
|
||||||
|
|
||||||
# content of conftest.py
|
# content of conftest.py
|
||||||
import pytest
|
import pytest
|
||||||
import smtplib
|
import smtplib
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
@pytest.fixture(scope="module")
|
||||||
def smtp(request):
|
def smtp(request):
|
||||||
smtp = smtplib.SMTP("merlinux.eu")
|
server = getattr(request.module, "smtpserver", "merlinux.eu")
|
||||||
|
smtp = smtplib.SMTP(server)
|
||||||
def fin():
|
def fin():
|
||||||
print ("finalizing %s" % smtp)
|
print ("finalizing %s" % smtp)
|
||||||
smtp.close()
|
smtp.close()
|
||||||
|
@ -260,19 +220,67 @@ The registered ``fin`` function will be called when the last test
|
||||||
using it has executed::
|
using it has executed::
|
||||||
|
|
||||||
$ py.test -s -q --tb=no
|
$ py.test -s -q --tb=no
|
||||||
collecting ... collected 2 items
|
|
||||||
FF
|
FF
|
||||||
2 failed in 0.21 seconds
|
finalizing <smtplib.SMTP instance at 0x1e15a70>
|
||||||
finalizing <smtplib.SMTP instance at 0x29f7908>
|
|
||||||
|
|
||||||
We see that the ``smtp`` instance is finalized after all
|
We see that the ``smtp`` instance is finalized after the two
|
||||||
tests executed. If we had specified ``scope='function'``
|
tests using it tests executed. If we had specified ``scope='function'``
|
||||||
then fixture setup and cleanup would occur around each
|
then fixture setup and cleanup would occur around each single test.
|
||||||
single test.
|
Note that the test module itself did not need to change!
|
||||||
|
|
||||||
Parametrizing a session-shared funcarg resource
|
Let's quickly create another test module that actually sets the
|
||||||
|
server URL and has a test to verify the fixture picks it up::
|
||||||
|
|
||||||
|
# content of test_anothersmtp.py
|
||||||
|
|
||||||
|
smtpserver = "mail.python.org" # will be read by smtp fixture
|
||||||
|
|
||||||
|
def test_showhelo(smtp):
|
||||||
|
assert 0, smtp.helo()
|
||||||
|
|
||||||
|
Running it::
|
||||||
|
|
||||||
|
$ py.test -qq --tb=short test_anothersmtp.py
|
||||||
|
F
|
||||||
|
================================= FAILURES =================================
|
||||||
|
______________________________ test_showhelo _______________________________
|
||||||
|
test_anothersmtp.py:5: in test_showhelo
|
||||||
|
> assert 0, smtp.helo()
|
||||||
|
E AssertionError: (250, 'mail.python.org')
|
||||||
|
|
||||||
|
**Test classes, modules or whole projects can make use of
|
||||||
|
one or more fixtures**. All required fixture functions will execute
|
||||||
|
before a test from the specifying context executes. As You can use this
|
||||||
|
to make tests operate from a pre-initialized directory or with
|
||||||
|
certain environment variables or with pre-configured global application
|
||||||
|
settings.
|
||||||
|
|
||||||
|
For example, the Django_ project requires database
|
||||||
|
initialization to be able to import from and use its model objects.
|
||||||
|
For that, the `pytest-django`_ plugin provides fixtures which your
|
||||||
|
project can then easily depend or extend on, simply by referencing the
|
||||||
|
name of the particular fixture.
|
||||||
|
|
||||||
|
|
||||||
|
**Fixture functions have limited visilibity** which depends on where they
|
||||||
|
are defined. If they are defined on a test class, only its test methods
|
||||||
|
may use it. A fixture defined in a module can only be used
|
||||||
|
from that test module. A fixture defined in a conftest.py file
|
||||||
|
can only be used by the tests below the directory of that file.
|
||||||
|
Lastly, plugins can define fixtures which are available across all
|
||||||
|
projects.
|
||||||
|
|
||||||
|
|
||||||
|
Parametrizing a session-shared fixture
|
||||||
-----------------------------------------------------------------
|
-----------------------------------------------------------------
|
||||||
|
|
||||||
|
**Fixture functions can be parametrized** in which case they will be called
|
||||||
|
multiple times, each time executing the set of dependent tests, i. e. the
|
||||||
|
tests that depend on this fixture. Test functions do usually not need
|
||||||
|
to be aware of their re-running. Fixture parametrization helps to
|
||||||
|
write exhaustive functional tests for components which themselves can be
|
||||||
|
configured in multiple ways.
|
||||||
|
|
||||||
Extending the previous example, we can flag the fixture to create
|
Extending the previous example, we can flag the fixture to create
|
||||||
two ``smtp`` fixture instances which will cause all tests using the
|
two ``smtp`` fixture instances which will cause all tests using the
|
||||||
fixture to run twice. The fixture function gets
|
fixture to run twice. The fixture function gets
|
||||||
|
@ -297,13 +305,12 @@ for each of which the fixture function will execute and can access
|
||||||
a value via ``request.param``. No test function code needs to change.
|
a value via ``request.param``. No test function code needs to change.
|
||||||
So let's just do another run::
|
So let's just do another run::
|
||||||
|
|
||||||
$ py.test -q
|
$ py.test -q test_module.py
|
||||||
collecting ... collected 4 items
|
|
||||||
FFFF
|
FFFF
|
||||||
================================= FAILURES =================================
|
================================= FAILURES =================================
|
||||||
__________________________ test_ehlo[merlinux.eu] __________________________
|
__________________________ test_ehlo[merlinux.eu] __________________________
|
||||||
|
|
||||||
smtp = <smtplib.SMTP instance at 0x1c261b8>
|
smtp = <smtplib.SMTP instance at 0x27ae998>
|
||||||
|
|
||||||
def test_ehlo(smtp):
|
def test_ehlo(smtp):
|
||||||
response = smtp.ehlo()
|
response = smtp.ehlo()
|
||||||
|
@ -312,10 +319,10 @@ So let's just do another run::
|
||||||
> assert 0 # for demo purposes
|
> assert 0 # for demo purposes
|
||||||
E assert 0
|
E assert 0
|
||||||
|
|
||||||
test_module.py:5: AssertionError
|
test_module.py:6: AssertionError
|
||||||
__________________________ test_noop[merlinux.eu] __________________________
|
__________________________ test_noop[merlinux.eu] __________________________
|
||||||
|
|
||||||
smtp = <smtplib.SMTP instance at 0x1c261b8>
|
smtp = <smtplib.SMTP instance at 0x27ae998>
|
||||||
|
|
||||||
def test_noop(smtp):
|
def test_noop(smtp):
|
||||||
response = smtp.noop()
|
response = smtp.noop()
|
||||||
|
@ -323,10 +330,10 @@ So let's just do another run::
|
||||||
> assert 0 # for demo purposes
|
> assert 0 # for demo purposes
|
||||||
E assert 0
|
E assert 0
|
||||||
|
|
||||||
test_module.py:10: AssertionError
|
test_module.py:11: AssertionError
|
||||||
________________________ test_ehlo[mail.python.org] ________________________
|
________________________ test_ehlo[mail.python.org] ________________________
|
||||||
|
|
||||||
smtp = <smtplib.SMTP instance at 0x1c2a4d0>
|
smtp = <smtplib.SMTP instance at 0x28395f0>
|
||||||
|
|
||||||
def test_ehlo(smtp):
|
def test_ehlo(smtp):
|
||||||
response = smtp.ehlo()
|
response = smtp.ehlo()
|
||||||
|
@ -334,10 +341,10 @@ So let's just do another run::
|
||||||
> assert "merlinux" in response[1]
|
> assert "merlinux" in response[1]
|
||||||
E assert 'merlinux' in 'mail.python.org\nSIZE 10240000\nETRN\nSTARTTLS\nENHANCEDSTATUSCODES\n8BITMIME\nDSN'
|
E assert 'merlinux' in 'mail.python.org\nSIZE 10240000\nETRN\nSTARTTLS\nENHANCEDSTATUSCODES\n8BITMIME\nDSN'
|
||||||
|
|
||||||
test_module.py:4: AssertionError
|
test_module.py:5: AssertionError
|
||||||
________________________ test_noop[mail.python.org] ________________________
|
________________________ test_noop[mail.python.org] ________________________
|
||||||
|
|
||||||
smtp = <smtplib.SMTP instance at 0x1c2a4d0>
|
smtp = <smtplib.SMTP instance at 0x28395f0>
|
||||||
|
|
||||||
def test_noop(smtp):
|
def test_noop(smtp):
|
||||||
response = smtp.noop()
|
response = smtp.noop()
|
||||||
|
@ -345,42 +352,17 @@ So let's just do another run::
|
||||||
> assert 0 # for demo purposes
|
> assert 0 # for demo purposes
|
||||||
E assert 0
|
E assert 0
|
||||||
|
|
||||||
test_module.py:10: AssertionError
|
test_module.py:11: AssertionError
|
||||||
4 failed in 6.62 seconds
|
|
||||||
|
|
||||||
We now get four failures because we are running the two tests twice with
|
We now get four failures because we are running the two tests twice with
|
||||||
different ``smtp`` fixture instances. Note that with the
|
different ``smtp`` fixture instances. Note that with the
|
||||||
``mail.python.org`` connection the second test fails in ``test_ehlo``
|
``mail.python.org`` connection the second test fails in ``test_ehlo``
|
||||||
because it expects a specific server string.
|
because it expects a specific server string.
|
||||||
|
|
||||||
We also see that the two ``smtp`` instances are finalized appropriately.
|
|
||||||
|
|
||||||
Looking at test collection without running tests
|
|
||||||
------------------------------------------------------
|
|
||||||
|
|
||||||
You can also look at the tests which pytest collects without running them::
|
|
||||||
|
|
||||||
$ py.test --collectonly
|
|
||||||
=========================== test session starts ============================
|
|
||||||
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev18
|
|
||||||
plugins: xdist, bugzilla, cache, oejskit, cli, pep8, cov, timeout
|
|
||||||
collecting ... collected 4 items
|
|
||||||
<Module 'test_module.py'>
|
|
||||||
<Function 'test_ehlo[merlinux.eu]'>
|
|
||||||
<Function 'test_noop[merlinux.eu]'>
|
|
||||||
<Function 'test_ehlo[mail.python.org]'>
|
|
||||||
<Function 'test_noop[mail.python.org]'>
|
|
||||||
|
|
||||||
============================= in 0.01 seconds =============================
|
|
||||||
|
|
||||||
Our fixture parameters show up in the test id of the test functions.
|
|
||||||
Note that pytest orders your test run by resource usage, minimizing
|
|
||||||
the number of active resources at any given time.
|
|
||||||
|
|
||||||
|
|
||||||
.. _`interdependent fixtures`:
|
.. _`interdependent fixtures`:
|
||||||
|
|
||||||
Interdepdendent fixtures
|
Using fixtures from a fixture function
|
||||||
----------------------------------------------------------
|
----------------------------------------------------------
|
||||||
|
|
||||||
You can not only use fixtures in test functions but fixture functions
|
You can not only use fixtures in test functions but fixture functions
|
||||||
|
@ -410,15 +392,13 @@ Here we declare an ``app`` fixture which receives the previously defined
|
||||||
|
|
||||||
$ py.test -v test_appsetup.py
|
$ py.test -v test_appsetup.py
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev18 -- /home/hpk/venv/1/bin/python
|
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 -- /home/hpk/p/pytest/.tox/regen/bin/python
|
||||||
cachedir: /tmp/doc-exec-6/.cache
|
|
||||||
plugins: xdist, bugzilla, cache, oejskit, cli, pep8, cov, timeout
|
|
||||||
collecting ... collected 2 items
|
collecting ... collected 2 items
|
||||||
|
|
||||||
test_appsetup.py:12: test_smtp_exists[merlinux.eu] PASSED
|
test_appsetup.py:12: test_smtp_exists[merlinux.eu] PASSED
|
||||||
test_appsetup.py:12: test_smtp_exists[mail.python.org] PASSED
|
test_appsetup.py:12: test_smtp_exists[mail.python.org] PASSED
|
||||||
|
|
||||||
========================= 2 passed in 0.14 seconds =========================
|
========================= 2 passed in 0.09 seconds =========================
|
||||||
|
|
||||||
Due to the parametrization of ``smtp`` the test will run twice with two
|
Due to the parametrization of ``smtp`` the test will run twice with two
|
||||||
different ``App`` instances and respective smtp servers. There is no
|
different ``App`` instances and respective smtp servers. There is no
|
||||||
|
@ -429,6 +409,7 @@ session-scoped ``smtp``: it is fine for fixtures to use "broader" scoped
|
||||||
fixtures but not the other way round: A session-scoped fixture could
|
fixtures but not the other way round: A session-scoped fixture could
|
||||||
not use a module-scoped one in a meaningful way.
|
not use a module-scoped one in a meaningful way.
|
||||||
|
|
||||||
|
|
||||||
.. _`automatic per-resource grouping`:
|
.. _`automatic per-resource grouping`:
|
||||||
|
|
||||||
Automatic grouping of tests by fixture instances
|
Automatic grouping of tests by fixture instances
|
||||||
|
@ -473,9 +454,7 @@ Let's run the tests in verbose mode and with looking at the print-output::
|
||||||
|
|
||||||
$ py.test -v -s test_module.py
|
$ py.test -v -s test_module.py
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev18 -- /home/hpk/venv/1/bin/python
|
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 -- /home/hpk/p/pytest/.tox/regen/bin/python
|
||||||
cachedir: /tmp/doc-exec-6/.cache
|
|
||||||
plugins: xdist, bugzilla, cache, oejskit, cli, pep8, cov, timeout
|
|
||||||
collecting ... collected 8 items
|
collecting ... collected 8 items
|
||||||
|
|
||||||
test_module.py:16: test_0[1] PASSED
|
test_module.py:16: test_0[1] PASSED
|
||||||
|
@ -487,7 +466,7 @@ Let's run the tests in verbose mode and with looking at the print-output::
|
||||||
test_module.py:20: test_2[1-mod2] PASSED
|
test_module.py:20: test_2[1-mod2] PASSED
|
||||||
test_module.py:20: test_2[2-mod2] PASSED
|
test_module.py:20: test_2[2-mod2] PASSED
|
||||||
|
|
||||||
========================= 8 passed in 0.02 seconds =========================
|
========================= 8 passed in 0.01 seconds =========================
|
||||||
test0 1
|
test0 1
|
||||||
test0 2
|
test0 2
|
||||||
create mod1
|
create mod1
|
||||||
|
@ -505,18 +484,20 @@ You can see that the parametrized module-scoped ``modarg`` resource caused
|
||||||
an ordering of test execution that lead to the fewest possible "active" resources. The finalizer for the ``mod1`` parametrized resource was executed
|
an ordering of test execution that lead to the fewest possible "active" resources. The finalizer for the ``mod1`` parametrized resource was executed
|
||||||
before the ``mod2`` resource was setup.
|
before the ``mod2`` resource was setup.
|
||||||
|
|
||||||
|
.. _`usefixtures`:
|
||||||
|
|
||||||
Marking test classes, modules, projects with required fixtures
|
|
||||||
|
using fixtures from classes, modules or projects
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
|
|
||||||
.. regendoc:wipe
|
.. regendoc:wipe
|
||||||
|
|
||||||
Sometimes test functions do not directly get access to a fixture object.
|
Sometimes test functions do not directly need access to a fixture object.
|
||||||
For example, each test in a test class may require to operate with an
|
For example, tests may require to operate with an
|
||||||
empty directory as the current working directory. Here is how you can
|
empty directory as the current working directory. Here is how you can
|
||||||
can use the standard :ref:`tempfile <lib:tempfile>` and pytest fixtures
|
can use the standard `tempfile <http://docs.python.org/library/tempfile.html>`_ and pytest fixtures to
|
||||||
to achieve it. We separate the creation of the fixture into
|
achieve it. We separate the creation of the fixture into a conftest.py
|
||||||
a conftest.py file::
|
file::
|
||||||
|
|
||||||
# content of conftest.py
|
# content of conftest.py
|
||||||
|
|
||||||
|
@ -529,7 +510,7 @@ a conftest.py file::
|
||||||
newpath = tempfile.mkdtemp()
|
newpath = tempfile.mkdtemp()
|
||||||
os.chdir(newpath)
|
os.chdir(newpath)
|
||||||
|
|
||||||
and declare its use in a test module via a ``needs`` marker::
|
and declare its use in a test module via a ``usefixtures`` marker::
|
||||||
|
|
||||||
# content of test_setenv.py
|
# content of test_setenv.py
|
||||||
import os
|
import os
|
||||||
|
@ -546,20 +527,18 @@ and declare its use in a test module via a ``needs`` marker::
|
||||||
assert os.listdir(os.getcwd()) == []
|
assert os.listdir(os.getcwd()) == []
|
||||||
|
|
||||||
Due to the ``usefixtures`` marker, the ``cleandir`` fixture
|
Due to the ``usefixtures`` marker, the ``cleandir`` fixture
|
||||||
will be required for the execution of each of the test methods, just as if
|
will be required for the execution of each test method, just as if
|
||||||
you specified a "cleandir" function argument to each of them. Let's run it
|
you specified a "cleandir" function argument to each of them. Let's run it
|
||||||
to verify our fixture is activated::
|
to verify our fixture is activated and the tests pass::
|
||||||
|
|
||||||
$ py.test -q
|
$ py.test -q
|
||||||
collecting ... collected 2 items
|
|
||||||
..
|
..
|
||||||
2 passed in 0.02 seconds
|
|
||||||
|
|
||||||
You may specify the need for multiple fixtures::
|
You can specify multiple fixtures like this::
|
||||||
|
|
||||||
@pytest.mark.usefixtures("cleandir", "anotherfixture")
|
@pytest.mark.usefixtures("cleandir", "anotherfixture")
|
||||||
|
|
||||||
and you may specify fixture needs at the test module level, using
|
and you may specify fixture usage at the test module level, using
|
||||||
a generic feature of the mark mechanism::
|
a generic feature of the mark mechanism::
|
||||||
|
|
||||||
pytestmark = pytest.mark.usefixtures("cleandir")
|
pytestmark = pytest.mark.usefixtures("cleandir")
|
||||||
|
@ -572,18 +551,19 @@ into an ini-file::
|
||||||
[pytest]
|
[pytest]
|
||||||
usefixtures = cleandir
|
usefixtures = cleandir
|
||||||
|
|
||||||
|
.. _`autoactive fixtures`:
|
||||||
|
|
||||||
autoactive fixtures at class/module/directory/global level
|
autoactive fixtures (xUnit setup on steroids)
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
|
|
||||||
.. regendoc:wipe
|
.. regendoc:wipe
|
||||||
|
|
||||||
Occasionally, you may want to have fixtures get invoked automatically
|
Occasionally, you may want to have fixtures get invoked automatically
|
||||||
without any ``usefixtures`` or funcargs reference. As a practical example,
|
without a `usefixtures`_ or `funcargs`_ reference. As a practical
|
||||||
suppose we have a database fixture which has a begin/rollback/commit
|
example, suppose we have a database fixture which has a
|
||||||
architecture and we want to automatically surround each test method by a
|
begin/rollback/commit architecture and we want to automatically surround
|
||||||
transaction and a rollback. Here is a dummy self-contained implementation
|
each test method by a transaction and a rollback. Here is a dummy
|
||||||
of this idea::
|
self-contained implementation of this idea::
|
||||||
|
|
||||||
# content of test_db_transact.py
|
# content of test_db_transact.py
|
||||||
|
|
||||||
|
@ -617,9 +597,7 @@ specify it.
|
||||||
If we run it, we get two passing tests::
|
If we run it, we get two passing tests::
|
||||||
|
|
||||||
$ py.test -q
|
$ py.test -q
|
||||||
collecting ... collected 2 items
|
|
||||||
..
|
..
|
||||||
2 passed in 0.02 seconds
|
|
||||||
|
|
||||||
And here is how autoactive fixtures work in other scopes:
|
And here is how autoactive fixtures work in other scopes:
|
||||||
|
|
||||||
|
@ -658,9 +636,20 @@ and then have a TestClass using it by declaring the need::
|
||||||
While all test methods in this TestClass will use the transaction
|
While all test methods in this TestClass will use the transaction
|
||||||
fixture, other test classes or function will not do so without a marker or funcarg.
|
fixture, other test classes or function will not do so without a marker or funcarg.
|
||||||
|
|
||||||
|
controlled visibility of fixture functions
|
||||||
|
----------------------------------------------------
|
||||||
|
|
||||||
|
If during implementing your tests you realize that you
|
||||||
|
want to use a fixture function from multiple test files you can move it
|
||||||
|
to a :ref:`conftest.py <conftest.py>` file or even separately installable
|
||||||
|
:ref:`plugins <plugins>` without changing test code. The discovery of
|
||||||
|
fixtures functions starts at test classes, then test modules, then
|
||||||
|
``conftest.py`` files and finally builtin and third party plugins.
|
||||||
|
|
||||||
.. currentmodule:: _pytest.python
|
.. currentmodule:: _pytest.python
|
||||||
|
|
||||||
.. _`@pytest.fixture`:
|
.. _`@pytest.fixture`:
|
||||||
|
.. _`pytest.fixture`:
|
||||||
|
|
||||||
``@pytest.fixture``: marking a fixture function
|
``@pytest.fixture``: marking a fixture function
|
||||||
--------------------------------------------------------------
|
--------------------------------------------------------------
|
||||||
|
|
|
@ -1,18 +1,19 @@
|
||||||
|
|
||||||
.. _`funcargcompare`:
|
.. _`funcargcompare`:
|
||||||
|
|
||||||
=============================================================
|
|
||||||
pytest-2.3: reasoning for the new funcarg and setup functions
|
pytest-2.3: reasoning for the new funcarg and setup functions
|
||||||
=============================================================
|
=============================================================
|
||||||
|
|
||||||
**Target audience**: Reading this document requires basic knowledge of
|
**Target audience**: Reading this document requires basic knowledge of
|
||||||
python testing, xUnit setup methods and the (previous) basic pytest
|
python testing, xUnit setup methods and the (previous) basic pytest
|
||||||
funcarg mechanism, see http://pytest.org/2.2.4/funcargs.html
|
funcarg mechanism, see http://pytest.org/2.2.4/funcargs.html
|
||||||
|
If you are new to pytest, then you can simply ignore this
|
||||||
|
section and read the other sections.
|
||||||
|
|
||||||
.. currentmodule:: _pytest
|
.. currentmodule:: _pytest
|
||||||
|
|
||||||
Shortcomings of the previous pytest_funcarg__ mechanism
|
Shortcomings of the previous ``pytest_funcarg__`` mechanism
|
||||||
===========================================================
|
--------------------------------------------------------------
|
||||||
|
|
||||||
The pre pytest-2.3 funcarg mechanism calls a factory each time a
|
The pre pytest-2.3 funcarg mechanism calls a factory each time a
|
||||||
funcarg for a test function is required. If a factory wants to
|
funcarg for a test function is required. If a factory wants to
|
||||||
|
@ -58,12 +59,15 @@ There are several limitations and difficulties with this approach:
|
||||||
funcarg resource if it isn't stated in the test function signature.
|
funcarg resource if it isn't stated in the test function signature.
|
||||||
|
|
||||||
All of these limitations are addressed with pytest-2.3 and its
|
All of these limitations are addressed with pytest-2.3 and its
|
||||||
new facilities.
|
improved :ref:`fixture mechanism <fixture>`.
|
||||||
|
|
||||||
Direct scoping of funcarg factories
|
|
||||||
|
Direct scoping of fixture/funcarg factories
|
||||||
--------------------------------------------------------
|
--------------------------------------------------------
|
||||||
|
|
||||||
Instead of calling cached_setup(), you can use the :ref:`@pytest.fixture <@pytest.fixture>` decorator and directly state the scope::
|
Instead of calling cached_setup() with a cache scope, you can use the
|
||||||
|
:ref:`@pytest.fixture <pytest.fixture>` decorator and directly state
|
||||||
|
the scope::
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
@pytest.fixture(scope="session")
|
||||||
def db(request):
|
def db(request):
|
||||||
|
@ -142,7 +146,7 @@ But it is then not possible to define scoping and parametrization.
|
||||||
It is thus recommended to use the factory decorator.
|
It is thus recommended to use the factory decorator.
|
||||||
|
|
||||||
|
|
||||||
solving per-session setup / the new @setup marker
|
solving per-session setup / autoactive fixtures
|
||||||
--------------------------------------------------------------
|
--------------------------------------------------------------
|
||||||
|
|
||||||
pytest for a long time offered a pytest_configure and a pytest_sessionstart
|
pytest for a long time offered a pytest_configure and a pytest_sessionstart
|
||||||
|
@ -169,17 +173,44 @@ during test execution and parametrization happens at collection time.
|
||||||
|
|
||||||
It follows that pytest_configure/session/runtest_setup are often not
|
It follows that pytest_configure/session/runtest_setup are often not
|
||||||
appropriate for implementing common fixture needs. Therefore,
|
appropriate for implementing common fixture needs. Therefore,
|
||||||
pytest-2.3 introduces a new :ref:`@pytest.setup <setup>` marker
|
pytest-2.3 introduces :ref:`autoactive fixtures` which fully
|
||||||
for setup functions and it accepts an optional "scope" parameter.
|
integrate with the generic :ref:`fixture mechanism <fixture>`
|
||||||
|
and obsolete many prior uses of pytest hooks.
|
||||||
|
|
||||||
See :ref:`setup` for more explanation and examples.
|
funcargs/fixture discovery now happens at collection time
|
||||||
|
|
||||||
funcarg and setup discovery now happens at collection time
|
|
||||||
---------------------------------------------------------------------
|
---------------------------------------------------------------------
|
||||||
|
|
||||||
pytest-2.3 takes care to discover funcarg factories and @setup methods
|
pytest-2.3 takes care to discover fixture/funcarg factories
|
||||||
at collection time. This is more efficient especially for large test suites.
|
at collection time. This is more efficient especially for large test suites.
|
||||||
Moreover, a call to "py.test --collectonly" should be able to in the future
|
Moreover, a call to "py.test --collectonly" should be able to in the future
|
||||||
show a lot of setup-information and thus presents a nice method to get an
|
show a lot of setup-information and thus presents a nice method to get an
|
||||||
overview of resource management in your project.
|
overview of fixture management in your project.
|
||||||
|
|
||||||
|
.. _`compatibility notes`:
|
||||||
|
|
||||||
|
.. _`funcargscompat`:
|
||||||
|
|
||||||
|
Conclusion and compatibility notes
|
||||||
|
---------------------------------------------------------
|
||||||
|
|
||||||
|
**Fixtures** were originally introduced to pytest-2.0. In pytest-2.3
|
||||||
|
the mechanism was extended and refined:
|
||||||
|
|
||||||
|
* previously funcarg factories were specified with a special
|
||||||
|
``pytest_funcarg__NAME`` prefix instead of using the
|
||||||
|
``@pytest.fixture`` decorator.
|
||||||
|
|
||||||
|
* Factories received a :ref:`request <request>` object which managed caching through
|
||||||
|
``request.cached_setup()`` calls and allowed using other funcargs via
|
||||||
|
``request.getfuncargvalue()`` calls. These intricate APIs made it hard
|
||||||
|
to do proper parametrization and implement resource caching. The
|
||||||
|
new ``@pytest.fixture`` decorator allows to simply declare the scope
|
||||||
|
and let pytest figure things out for you.
|
||||||
|
|
||||||
|
* if you used parametrization and funcarg factories which made use of
|
||||||
|
``request.cached_setup()`` it is recommeneded to invest a few minutes
|
||||||
|
and simplify your fixture function code to use the :ref:`@pytest.fixture`
|
||||||
|
decorator instead. This will also allow to take advantage of
|
||||||
|
the automatic per-resource grouping of tests.
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,392 +1,14 @@
|
||||||
|
|
||||||
.. _resources:
|
|
||||||
.. _`funcargs`:
|
|
||||||
.. _`funcarg mechanism`:
|
|
||||||
|
|
||||||
=======================================================
|
=======================================================
|
||||||
funcargs: resource injection and parametrization
|
funcargs: resource injection and parametrization
|
||||||
=======================================================
|
=======================================================
|
||||||
|
|
||||||
.. note::
|
pytest-2.3 introduces major refinements to fixture management
|
||||||
|
of which the funcarg mechanism introduced with pytest-2.0 remains
|
||||||
pytest-2.3 introduces major refinements to the test setup and funcarg
|
a core part. The documentation has been refactored as well
|
||||||
mechanisms introduced to pytest-2.0. All pre-2.3 usages remain
|
and you can read on here:
|
||||||
supported and several use cases, among them scoping and parametrization
|
|
||||||
of funcarg resources, are now easier to accomplish. For more background,
|
- :ref:`fixtures`
|
||||||
see `compatibility notes`_ and the detailed :ref:`reasoning for the new
|
- :ref:`parametrize`
|
||||||
funcarg and setup functions <funcargcompare>`.
|
- :ref:`funcargcompare`
|
||||||
|
|
||||||
.. _`Dependency injection`: http://en.wikipedia.org/wiki/Dependency_injection
|
|
||||||
|
|
||||||
Introduction
|
|
||||||
====================
|
|
||||||
|
|
||||||
pytest supports the injection of test resources into test and setup functions
|
|
||||||
and flexibly control their life cycle in relation to the overall test
|
|
||||||
execution. Moreover, tests can get executed multiple times if you have
|
|
||||||
different variants of test resources to test with.
|
|
||||||
|
|
||||||
The basic mechanism for injecting objects is called the *funcarg
|
|
||||||
mechanism* because objects are injected when a test or setup
|
|
||||||
**function** states it as an **argument**. The injected argument
|
|
||||||
is created by a call to a registered **fixture function** for each argument
|
|
||||||
name. This mechanism is an example of `Dependency Injection`_
|
|
||||||
and helps to de-couple test code from the setup of required
|
|
||||||
objects: at test writing time you do not need to care for the details of
|
|
||||||
where and how your required test resources are constructed, if they are
|
|
||||||
shared on a per-class, module or session basis, or if your test function
|
|
||||||
is invoked multiple times with differently configured resource
|
|
||||||
instances.
|
|
||||||
|
|
||||||
Fixture dependency injection allows to organise test resources
|
|
||||||
in a modular explicit way so that test functions state their needs
|
|
||||||
in their signature. pytest additionally offers powerful xunit-style
|
|
||||||
:ref:`setup functions <setup functions>` for the cases where you need
|
|
||||||
to create implicit test state that is not passed explicitely to test functions.
|
|
||||||
|
|
||||||
When a test function is invoked multiple times with different arguments we
|
|
||||||
speak of **parametrized testing**. You can use it e. g. to repeatedly run test
|
|
||||||
functions against different database backends or to check that certain
|
|
||||||
inputs lead to certain outputs.
|
|
||||||
|
|
||||||
Concretely, there are three main means of funcarg management:
|
|
||||||
|
|
||||||
* a `@pytest.fixture`_ marker to define resource factories,
|
|
||||||
their scoping and parametrization. Factories can themselves
|
|
||||||
receive resources through their function arguments, easing
|
|
||||||
the setup of `interdependent resources`_. Factories can use
|
|
||||||
the special `request`_ object to access details from where
|
|
||||||
the factory or setup function is called and for registering finalizers.
|
|
||||||
|
|
||||||
* a `@pytest.mark.parametrize`_ marker for executing test functions
|
|
||||||
multiple times with different argument sets,
|
|
||||||
|
|
||||||
* a `pytest_generate_tests`_ plugin hook marker for implementing
|
|
||||||
your parametrization for a test function which may depend on
|
|
||||||
command line options, class/module attributes etc.
|
|
||||||
|
|
||||||
Apart from making it easy to manage your own test resources
|
|
||||||
pytest also comes with some :ref:`builtinresources` which
|
|
||||||
you can use without defining them yourself. Third-party plugins
|
|
||||||
offer yet more domain-specific funcarg resources (for example the
|
|
||||||
`pytest-django plugin <http://pypi.python.org/pypi/pytest-django>`_) so
|
|
||||||
that after plugin installation you can simply use them in
|
|
||||||
your test and setup functions. This all contributes to high
|
|
||||||
re-useability of test resource management and goes far beyond what can
|
|
||||||
be done with the classical xUnit style approach which encodes resource
|
|
||||||
setup statically into the test source code, leading to duplicate and
|
|
||||||
hard-to change fixtures.
|
|
||||||
|
|
||||||
.. _`@pytest.fixture`:
|
|
||||||
|
|
||||||
``@pytest.fixture``: Creating parametrized, scoped resources
|
|
||||||
=====================================================================
|
|
||||||
|
|
||||||
Basic funcarg injection example
|
|
||||||
-----------------------------------------------------------
|
|
||||||
|
|
||||||
Let's look at a simple self-contained test module using a factory
|
|
||||||
and a funcarg::
|
|
||||||
|
|
||||||
# content of ./test_simplefactory.py
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
@pytest.fixture()
|
|
||||||
def myfuncarg():
|
|
||||||
return 42
|
|
||||||
|
|
||||||
def test_function(myfuncarg):
|
|
||||||
assert myfuncarg == 17
|
|
||||||
|
|
||||||
Here, the ``test_function`` needs an object named ``myfuncarg`` and thus
|
|
||||||
py.test will discover and call the ``@pytest.fixture`` marked ``myfuncarg``
|
|
||||||
factory function. Running the tests looks like this::
|
|
||||||
|
|
||||||
$ py.test test_simplefactory.py
|
|
||||||
=========================== test session starts ============================
|
|
||||||
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev11
|
|
||||||
plugins: xdist, bugzilla, cache, oejskit, cli, timeout, pep8, cov
|
|
||||||
collecting ... collected 1 items
|
|
||||||
|
|
||||||
test_simplefactory.py F
|
|
||||||
|
|
||||||
================================= FAILURES =================================
|
|
||||||
______________________________ test_function _______________________________
|
|
||||||
|
|
||||||
myfuncarg = 42
|
|
||||||
|
|
||||||
def test_function(myfuncarg):
|
|
||||||
> assert myfuncarg == 17
|
|
||||||
E assert 42 == 17
|
|
||||||
|
|
||||||
test_simplefactory.py:8: AssertionError
|
|
||||||
========================= 1 failed in 0.01 seconds =========================
|
|
||||||
|
|
||||||
This shows that the test function was called with a ``myfuncarg``
|
|
||||||
argument value of ``42`` and the assert fails as expected. Here is
|
|
||||||
how py.test comes to call the test function this way:
|
|
||||||
|
|
||||||
1. py.test :ref:`finds <test discovery>` the ``test_function`` because
|
|
||||||
of the ``test_`` prefix. The test function needs a function argument
|
|
||||||
named ``myfuncarg``. A matching factory function is discovered by
|
|
||||||
looking for a factory function named ``myfuncarg``.
|
|
||||||
|
|
||||||
2. ``myfuncarg()`` is called to create a value ``42``.
|
|
||||||
|
|
||||||
3. ``test_function(42)`` is now called and results in the above
|
|
||||||
reported exception because of the assertion mismatch.
|
|
||||||
|
|
||||||
Note that if you misspell a function argument or want
|
|
||||||
to use one that isn't available, you'll see an error
|
|
||||||
with a list of available function arguments.
|
|
||||||
|
|
||||||
.. Note::
|
|
||||||
|
|
||||||
You can always issue::
|
|
||||||
|
|
||||||
py.test --fixtures test_simplefactory.py
|
|
||||||
|
|
||||||
to see available function arguments.
|
|
||||||
|
|
||||||
|
|
||||||
Location independency of funcarg factories
|
|
||||||
----------------------------------------------------
|
|
||||||
|
|
||||||
If during implementing your tests you realize that you
|
|
||||||
want to use a factory from multiple test files you can move it
|
|
||||||
to a :ref:`conftest.py <conftest.py>` file or even separately installable
|
|
||||||
:ref:`plugins <plugins>` without changing test code. The discovery of
|
|
||||||
funcarg factories starts at test classes, then test modules, then
|
|
||||||
``conftest.py`` files and finally builtin and 3-rd party plugins.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.. _`test generators`:
|
|
||||||
.. _`parametrizing-tests`:
|
|
||||||
.. _`parametrized test functions`:
|
|
||||||
|
|
||||||
Parametrizing test functions
|
|
||||||
==========================================================================
|
|
||||||
|
|
||||||
While the `@pytest.fixture`_ decorator allows to define parametrization
|
|
||||||
of funcarg resources at the factory-level, there are also means to
|
|
||||||
define parametrization at test functions directly:
|
|
||||||
|
|
||||||
* `@pytest.mark.parametrize`_ to provide multiple argument sets
|
|
||||||
for a particular test function or class.
|
|
||||||
|
|
||||||
* `pytest_generate_tests`_ to implement your own custom parametrization
|
|
||||||
scheme or extensions.
|
|
||||||
|
|
||||||
.. _`@pytest.mark.parametrize`:
|
|
||||||
|
|
||||||
``@pytest.mark.parametrize``: parametrizing test functions
|
|
||||||
---------------------------------------------------------------------
|
|
||||||
|
|
||||||
.. regendoc: wipe
|
|
||||||
|
|
||||||
.. versionadded:: 2.2
|
|
||||||
|
|
||||||
The builtin ``pytest.mark.parametrize`` decorator enables
|
|
||||||
parametrization of arguments for a test function. Here is a typical example
|
|
||||||
of a test function that wants check for expected output given a certain input::
|
|
||||||
|
|
||||||
# content of test_expectation.py
|
|
||||||
import pytest
|
|
||||||
@pytest.mark.parametrize(("input", "expected"), [
|
|
||||||
("3+5", 8),
|
|
||||||
("2+4", 6),
|
|
||||||
("6*9", 42),
|
|
||||||
])
|
|
||||||
def test_eval(input, expected):
|
|
||||||
assert eval(input) == expected
|
|
||||||
|
|
||||||
The ``@parametrize`` decorator defines three different argument sets for the
|
|
||||||
two ``(input, output)`` arguments of ``test_eval`` function so the latter
|
|
||||||
will be run three times::
|
|
||||||
|
|
||||||
$ py.test -q
|
|
||||||
collecting ... collected 13 items
|
|
||||||
....F........
|
|
||||||
================================= FAILURES =================================
|
|
||||||
____________________________ test_eval[6*9-42] _____________________________
|
|
||||||
|
|
||||||
input = '6*9', expected = 42
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(("input", "expected"), [
|
|
||||||
("3+5", 8),
|
|
||||||
("2+4", 6),
|
|
||||||
("6*9", 42),
|
|
||||||
])
|
|
||||||
def test_eval(input, expected):
|
|
||||||
> assert eval(input) == expected
|
|
||||||
E assert 54 == 42
|
|
||||||
E + where 54 = eval('6*9')
|
|
||||||
|
|
||||||
test_expectation.py:8: AssertionError
|
|
||||||
1 failed, 12 passed in 6.41 seconds
|
|
||||||
|
|
||||||
As expected only one pair of input/output values fails the simple test function.
|
|
||||||
As usual you can see the ``input`` and ``output`` values in the traceback.
|
|
||||||
|
|
||||||
Note that there are various ways how you can mark groups of functions,
|
|
||||||
see :ref:`mark`.
|
|
||||||
|
|
||||||
|
|
||||||
.. _`pytest_generate_tests`:
|
|
||||||
|
|
||||||
Basic ``pytest_generate_tests`` example
|
|
||||||
---------------------------------------------
|
|
||||||
|
|
||||||
.. XXX
|
|
||||||
|
|
||||||
> line 598 "Basic ``pytest_generate_tests`` example" - I think this is
|
|
||||||
> not a very basic example! I think it is copied from parametrize.txt
|
|
||||||
> page, where it might make more sense. Here is what I would consider a
|
|
||||||
> basic example.
|
|
||||||
>
|
|
||||||
> # code
|
|
||||||
> def isSquare(n):
|
|
||||||
> n = n ** 0.5
|
|
||||||
> return int(n) == n
|
|
||||||
>
|
|
||||||
> # test file
|
|
||||||
> def pytest_generate_tests(metafunc):
|
|
||||||
> squares = [1, 4, 9, 16, 25, 36, 49]
|
|
||||||
> for n in range(1, 50):
|
|
||||||
> expected = n in squares
|
|
||||||
> if metafunc.function.__name__ == 'test_isSquare':
|
|
||||||
> metafunc.addcall(id=n, funcargs=dict(n=n,
|
|
||||||
> expected=expected))
|
|
||||||
>
|
|
||||||
>
|
|
||||||
> def test_isSquare(n, expected):
|
|
||||||
> assert isSquare(n) == expected
|
|
||||||
|
|
||||||
|
|
||||||
.. XXX
|
|
||||||
consider adding more examples, also mixed (factory-parametrized/test-function-parametrized, see mail from Brianna)
|
|
||||||
|
|
||||||
The ``pytest_generate_tests`` hook is typically used if you want
|
|
||||||
to go beyond what ``@pytest.mark.parametrize`` offers. For example,
|
|
||||||
let's say we want to execute a test with different computation
|
|
||||||
parameters and the parameter range shall be determined by a command
|
|
||||||
line argument. Let's first write a simple (do-nothing) computation test::
|
|
||||||
|
|
||||||
# content of test_compute.py
|
|
||||||
|
|
||||||
def test_compute(param1):
|
|
||||||
assert param1 < 4
|
|
||||||
|
|
||||||
Now we add a ``conftest.py`` file containing the addition of a
|
|
||||||
command line option and the generation of tests depending on
|
|
||||||
that option::
|
|
||||||
|
|
||||||
# content of conftest.py
|
|
||||||
|
|
||||||
def pytest_addoption(parser):
|
|
||||||
parser.addoption("--all", action="store_true",
|
|
||||||
help="run all combinations")
|
|
||||||
|
|
||||||
def pytest_generate_tests(metafunc):
|
|
||||||
if 'param1' in metafunc.fixturenames:
|
|
||||||
if metafunc.config.option.all:
|
|
||||||
end = 5
|
|
||||||
else:
|
|
||||||
end = 2
|
|
||||||
metafunc.parametrize("param1", range(end))
|
|
||||||
|
|
||||||
This means that we only run two tests if no option is passed::
|
|
||||||
|
|
||||||
$ py.test -q test_compute.py
|
|
||||||
collecting ... collected 2 items
|
|
||||||
..
|
|
||||||
2 passed in 0.01 seconds
|
|
||||||
|
|
||||||
And we run five tests if we add the ``--all`` option::
|
|
||||||
|
|
||||||
$ py.test -q --all test_compute.py
|
|
||||||
collecting ... collected 5 items
|
|
||||||
....F
|
|
||||||
================================= FAILURES =================================
|
|
||||||
_____________________________ test_compute[4] ______________________________
|
|
||||||
|
|
||||||
param1 = 4
|
|
||||||
|
|
||||||
def test_compute(param1):
|
|
||||||
> assert param1 < 4
|
|
||||||
E assert 4 < 4
|
|
||||||
|
|
||||||
test_compute.py:3: AssertionError
|
|
||||||
1 failed, 4 passed in 0.02 seconds
|
|
||||||
|
|
||||||
As expected when running the full range of ``param1`` values
|
|
||||||
we'll get an error on the last one.
|
|
||||||
|
|
||||||
You might want to look at :ref:`more parametrization examples <paramexamples>`.
|
|
||||||
|
|
||||||
|
|
||||||
.. _`metafunc object`:
|
|
||||||
|
|
||||||
The **metafunc** object
|
|
||||||
-------------------------------------------
|
|
||||||
|
|
||||||
metafunc objects are passed to the ``pytest_generate_tests`` hook.
|
|
||||||
They help to inspect a testfunction and to generate tests
|
|
||||||
according to test configuration or values specified
|
|
||||||
in the class or module where a test function is defined:
|
|
||||||
|
|
||||||
``metafunc.fixturenames``: set of required function arguments for given function
|
|
||||||
|
|
||||||
``metafunc.function``: underlying python test function
|
|
||||||
|
|
||||||
``metafunc.cls``: class object where the test function is defined in or None.
|
|
||||||
|
|
||||||
``metafunc.module``: the module object where the test function is defined in.
|
|
||||||
|
|
||||||
``metafunc.config``: access to command line opts and general config
|
|
||||||
|
|
||||||
``metafunc.funcargnames``: alias for ``fixturenames``, for pre-2.3 compatibility
|
|
||||||
|
|
||||||
.. automethod:: Metafunc.parametrize
|
|
||||||
.. automethod:: Metafunc.addcall(funcargs=None,id=_notexists,param=_notexists)
|
|
||||||
|
|
||||||
|
|
||||||
.. regendoc:wipe
|
|
||||||
|
|
||||||
|
|
||||||
.. _`compatibility notes`:
|
|
||||||
|
|
||||||
.. _`funcargscompat`:
|
|
||||||
|
|
||||||
Compatibility notes
|
|
||||||
============================================================
|
|
||||||
|
|
||||||
**Fixtures** were originally introduced to pytest-2.0. In pytest-2.3
|
|
||||||
the mechanism was extended and refined:
|
|
||||||
|
|
||||||
* previously funcarg factories were specified with a special
|
|
||||||
``pytest_funcarg__NAME`` prefix instead of using the
|
|
||||||
``@pytest.fixture`` decorator.
|
|
||||||
|
|
||||||
* Factories received a `request`_ object which managed caching through
|
|
||||||
``request.cached_setup()`` calls and allowed using other funcargs via
|
|
||||||
``request.getfuncargvalue()`` calls. These intricate APIs made it hard
|
|
||||||
to do proper parametrization and implement resource caching. The
|
|
||||||
new ``@pytest.fixture`` decorator allows to simply declare the scope
|
|
||||||
and let pytest figure things out for you.
|
|
||||||
|
|
||||||
* if you used parametrization and funcarg factories which made use of
|
|
||||||
``request.cached_setup()`` it is recommeneded to invest a few minutes
|
|
||||||
and simplify your fixture function code to use the `@pytest.fixture`_
|
|
||||||
decorator instead. This will also allow to take advantage of
|
|
||||||
the `automatic per-resource grouping`_ of tests.
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
Throughout the pytest documents the ``pytest_funcarg__NAME`` way of
|
|
||||||
defining a fixture function is often termed "old-style". Their
|
|
||||||
use remains fully supported and existing code using it should run
|
|
||||||
unmodified.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
Installation and Getting Started
|
Installation and Getting Started
|
||||||
===================================
|
===================================
|
||||||
|
|
||||||
**Pythons**: Python 2.4-3.2, Jython, PyPy
|
**Pythons**: Python 2.4-3.3, Jython, PyPy
|
||||||
|
|
||||||
**Platforms**: Unix/Posix and Windows
|
**Platforms**: Unix/Posix and Windows
|
||||||
|
|
||||||
|
@ -22,14 +22,7 @@ Installation options::
|
||||||
To check your installation has installed the correct version::
|
To check your installation has installed the correct version::
|
||||||
|
|
||||||
$ py.test --version
|
$ py.test --version
|
||||||
This is py.test version 2.3.0.dev2, imported from /home/hpk/p/pytest/pytest.pyc
|
This is py.test version 2.3.0.dev19, imported from /home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/pytest.pyc
|
||||||
setuptools registered plugins:
|
|
||||||
pytest-xdist-1.8 at /home/hpk/p/pytest-xdist/xdist/plugin.pyc
|
|
||||||
pytest-bugzilla-0.1 at /home/hpk/tmp/eanxgeek/pytest_bugzilla.pyc
|
|
||||||
pytest-cache-0.9 at /home/hpk/p/pytest-cache/pytest_cache.pyc
|
|
||||||
oejskit-0.9.0 at /home/hpk/p/js-infrastructure/oejskit/pytest_jstests.pyc
|
|
||||||
pytest-pep8-1.0.1 at /home/hpk/venv/1/local/lib/python2.7/site-packages/pytest_pep8.pyc
|
|
||||||
pytest-cov-1.6 at /home/hpk/venv/1/local/lib/python2.7/site-packages/pytest_cov.pyc
|
|
||||||
|
|
||||||
If you get an error checkout :ref:`installation issues`.
|
If you get an error checkout :ref:`installation issues`.
|
||||||
|
|
||||||
|
@ -51,9 +44,8 @@ That's it. You can execute the test function now::
|
||||||
|
|
||||||
$ py.test
|
$ py.test
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev2
|
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19
|
||||||
plugins: xdist, bugzilla, cache, oejskit, pep8, cov
|
collected 1 items
|
||||||
collecting ... collected 1 items
|
|
||||||
|
|
||||||
test_sample.py F
|
test_sample.py F
|
||||||
|
|
||||||
|
@ -66,7 +58,7 @@ That's it. You can execute the test function now::
|
||||||
E + where 4 = func(3)
|
E + where 4 = func(3)
|
||||||
|
|
||||||
test_sample.py:5: AssertionError
|
test_sample.py:5: AssertionError
|
||||||
========================= 1 failed in 0.02 seconds =========================
|
========================= 1 failed in 0.01 seconds =========================
|
||||||
|
|
||||||
py.test found the ``test_answer`` function by following :ref:`standard test discovery rules <test discovery>`, basically detecting the ``test_`` prefixes. We got a failure report because our little ``func(3)`` call did not return ``5``.
|
py.test found the ``test_answer`` function by following :ref:`standard test discovery rules <test discovery>`, basically detecting the ``test_`` prefixes. We got a failure report because our little ``func(3)`` call did not return ``5``.
|
||||||
|
|
||||||
|
@ -99,9 +91,7 @@ use the ``raises`` helper::
|
||||||
Running it with, this time in "quiet" reporting mode::
|
Running it with, this time in "quiet" reporting mode::
|
||||||
|
|
||||||
$ py.test -q test_sysexit.py
|
$ py.test -q test_sysexit.py
|
||||||
collecting ... collected 1 items
|
|
||||||
.
|
.
|
||||||
1 passed in 0.02 seconds
|
|
||||||
|
|
||||||
.. todo:: For further ways to assert exceptions see the `raises`
|
.. todo:: For further ways to assert exceptions see the `raises`
|
||||||
|
|
||||||
|
@ -127,12 +117,11 @@ There is no need to subclass anything. We can simply
|
||||||
run the module by passing its filename::
|
run the module by passing its filename::
|
||||||
|
|
||||||
$ py.test -q test_class.py
|
$ py.test -q test_class.py
|
||||||
collecting ... collected 2 items
|
|
||||||
.F
|
.F
|
||||||
================================= FAILURES =================================
|
================================= FAILURES =================================
|
||||||
____________________________ TestClass.test_two ____________________________
|
____________________________ TestClass.test_two ____________________________
|
||||||
|
|
||||||
self = <test_class.TestClass instance at 0x2343830>
|
self = <test_class.TestClass instance at 0x1ce8758>
|
||||||
|
|
||||||
def test_two(self):
|
def test_two(self):
|
||||||
x = "hello"
|
x = "hello"
|
||||||
|
@ -140,7 +129,6 @@ run the module by passing its filename::
|
||||||
E assert hasattr('hello', 'check')
|
E assert hasattr('hello', 'check')
|
||||||
|
|
||||||
test_class.py:8: AssertionError
|
test_class.py:8: AssertionError
|
||||||
1 failed, 1 passed in 0.02 seconds
|
|
||||||
|
|
||||||
The first test passed, the second failed. Again we can easily see
|
The first test passed, the second failed. Again we can easily see
|
||||||
the intermediate values used in the assertion, helping us to
|
the intermediate values used in the assertion, helping us to
|
||||||
|
@ -150,9 +138,9 @@ Going functional: requesting a unique temporary directory
|
||||||
--------------------------------------------------------------
|
--------------------------------------------------------------
|
||||||
|
|
||||||
For functional tests one often needs to create some files
|
For functional tests one often needs to create some files
|
||||||
and pass them to application objects. py.test provides
|
and pass them to application objects. pytest provides
|
||||||
the versatile :ref:`funcarg mechanism` which allows to request
|
:ref:`builtinfixtures` which allow to request arbitrary
|
||||||
arbitrary resources, for example a unique temporary directory::
|
resources, for example a unique temporary directory::
|
||||||
|
|
||||||
# content of test_tmpdir.py
|
# content of test_tmpdir.py
|
||||||
def test_needsfiles(tmpdir):
|
def test_needsfiles(tmpdir):
|
||||||
|
@ -160,16 +148,15 @@ arbitrary resources, for example a unique temporary directory::
|
||||||
assert 0
|
assert 0
|
||||||
|
|
||||||
We list the name ``tmpdir`` in the test function signature and
|
We list the name ``tmpdir`` in the test function signature and
|
||||||
py.test will lookup and call a factory to create the resource
|
py.test will lookup and call a fixture factory to create the resource
|
||||||
before performing the test function call. Let's just run it::
|
before performing the test function call. Let's just run it::
|
||||||
|
|
||||||
$ py.test -q test_tmpdir.py
|
$ py.test -q test_tmpdir.py
|
||||||
collecting ... collected 1 items
|
|
||||||
F
|
F
|
||||||
================================= FAILURES =================================
|
================================= FAILURES =================================
|
||||||
_____________________________ test_needsfiles ______________________________
|
_____________________________ test_needsfiles ______________________________
|
||||||
|
|
||||||
tmpdir = local('/home/hpk/tmp/pytest-2885/test_needsfiles0')
|
tmpdir = local('/tmp/pytest-168/test_needsfiles0')
|
||||||
|
|
||||||
def test_needsfiles(tmpdir):
|
def test_needsfiles(tmpdir):
|
||||||
print tmpdir
|
print tmpdir
|
||||||
|
@ -178,15 +165,14 @@ before performing the test function call. Let's just run it::
|
||||||
|
|
||||||
test_tmpdir.py:3: AssertionError
|
test_tmpdir.py:3: AssertionError
|
||||||
----------------------------- Captured stdout ------------------------------
|
----------------------------- Captured stdout ------------------------------
|
||||||
/home/hpk/tmp/pytest-2885/test_needsfiles0
|
/tmp/pytest-168/test_needsfiles0
|
||||||
1 failed in 0.22 seconds
|
|
||||||
|
|
||||||
Before the test runs, a unique-per-test-invocation temporary directory
|
Before the test runs, a unique-per-test-invocation temporary directory
|
||||||
was created. More info at :ref:`tmpdir handling`.
|
was created. More info at :ref:`tmpdir handling`.
|
||||||
|
|
||||||
You can find out what kind of builtin :ref:`funcargs` exist by typing::
|
You can find out what kind of builtin :ref:`fixtures` exist by typing::
|
||||||
|
|
||||||
py.test --fixtures # shows builtin and custom function arguments
|
py.test --fixtures # shows builtin and custom fixtures
|
||||||
|
|
||||||
Where to go next
|
Where to go next
|
||||||
-------------------------------------
|
-------------------------------------
|
||||||
|
|
|
@ -91,9 +91,6 @@ required for calling the test command. You can also pass additional
|
||||||
arguments to the subprocess-calls such as your test directory or other
|
arguments to the subprocess-calls such as your test directory or other
|
||||||
options.
|
options.
|
||||||
|
|
||||||
.. _`test discovery`:
|
|
||||||
.. _`Python test discovery`:
|
|
||||||
|
|
||||||
|
|
||||||
Integration with setuptools/distribute test commands
|
Integration with setuptools/distribute test commands
|
||||||
----------------------------------------------------
|
----------------------------------------------------
|
||||||
|
@ -129,6 +126,8 @@ Now if you run::
|
||||||
this will download py.test if needed and then run py.test
|
this will download py.test if needed and then run py.test
|
||||||
as you would expect it to.
|
as you would expect it to.
|
||||||
|
|
||||||
|
.. _`test discovery`:
|
||||||
|
.. _`Python test discovery`:
|
||||||
|
|
||||||
Conventions for Python test discovery
|
Conventions for Python test discovery
|
||||||
-------------------------------------------------
|
-------------------------------------------------
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
|
|
||||||
|
|
||||||
Welcome to pytest!
|
pytest: makes you a better programmer
|
||||||
=============================================
|
=============================================
|
||||||
|
|
||||||
- **a mature full-featured testing tool**
|
- **a mature full-featured Python testing tool**
|
||||||
|
|
||||||
- runs on Posix/Windows, Python 2.4-3.2, PyPy and Jython-2.5.1
|
- runs on Posix/Windows, Python 2.4-3.3, PyPy and Jython-2.5.1
|
||||||
- :ref:`comprehensive online <toc>` and `PDF documentation <pytest.pdf>`_
|
- :ref:`comprehensive online <toc>` and `PDF documentation <pytest.pdf>`_
|
||||||
- continuously `tested on many Python interpreters <http://hudson.testrun.org/view/pytest/job/pytest/>`_
|
- continuously `tested on many Python interpreters <http://hudson.testrun.org/view/pytest/job/pytest/>`_
|
||||||
- used in :ref:`many projects and organisations <projects>`, in test
|
- used in :ref:`many projects and organisations <projects>`, in test
|
||||||
|
@ -25,12 +25,9 @@ Welcome to pytest!
|
||||||
|
|
||||||
- **supports functional testing and complex test setups**
|
- **supports functional testing and complex test setups**
|
||||||
|
|
||||||
- (new in 2.3) :ref:`easy test resource management, scoping and
|
- (new in 2.3) :ref:`modular parametrizeable fixtures <fixture>`
|
||||||
parametrization <resources>`
|
- :ref:`marking and test selection <mark>`
|
||||||
- (new in 2.3) :ref:`setup functions`.
|
- :ref:`parametrized test functions <parametrized test functions>`
|
||||||
- (new in 2.2) :ref:`durations`
|
|
||||||
- (much improved in 2.2) :ref:`marking and test selection <mark>`
|
|
||||||
- (improved in 2.2) :ref:`parametrized test functions <parametrized test functions>`
|
|
||||||
- advanced :ref:`skip and xfail`
|
- advanced :ref:`skip and xfail`
|
||||||
- can :ref:`distribute tests to multiple CPUs <xdistcpu>` through :ref:`xdist plugin <xdist>`
|
- can :ref:`distribute tests to multiple CPUs <xdistcpu>` through :ref:`xdist plugin <xdist>`
|
||||||
- can :ref:`continuously re-run failing tests <looponfailing>`
|
- can :ref:`continuously re-run failing tests <looponfailing>`
|
||||||
|
|
|
@ -0,0 +1,188 @@
|
||||||
|
|
||||||
|
.. _`test generators`:
|
||||||
|
.. _`parametrizing-tests`:
|
||||||
|
.. _`parametrized test functions`:
|
||||||
|
.. _`parametrize`:
|
||||||
|
|
||||||
|
Parametrizing fixtures and test functions
|
||||||
|
==========================================================================
|
||||||
|
|
||||||
|
While the :ref:`@pytest.fixture` decorator allows to define parametrization
|
||||||
|
at the level of fixture functions, there are two more parametrizations:
|
||||||
|
|
||||||
|
* `@pytest.mark.parametrize`_ to provide multiple argument/fixture sets
|
||||||
|
for a particular test function or class.
|
||||||
|
|
||||||
|
* `pytest_generate_tests`_ to implement your own custom parametrization
|
||||||
|
scheme or extensions.
|
||||||
|
|
||||||
|
.. _`@pytest.mark.parametrize`:
|
||||||
|
|
||||||
|
``@pytest.mark.parametrize``: parametrizing test functions
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
|
||||||
|
.. regendoc: wipe
|
||||||
|
|
||||||
|
.. versionadded:: 2.2
|
||||||
|
|
||||||
|
The builtin ``pytest.mark.parametrize`` decorator enables
|
||||||
|
parametrization of arguments for a test function. Here is a typical example
|
||||||
|
of a test function that wants check for expected output given a certain input::
|
||||||
|
|
||||||
|
# content of test_expectation.py
|
||||||
|
import pytest
|
||||||
|
@pytest.mark.parametrize(("input", "expected"), [
|
||||||
|
("3+5", 8),
|
||||||
|
("2+4", 6),
|
||||||
|
("6*9", 42),
|
||||||
|
])
|
||||||
|
def test_eval(input, expected):
|
||||||
|
assert eval(input) == expected
|
||||||
|
|
||||||
|
The ``@parametrize`` decorator defines three different argument sets for the
|
||||||
|
two ``(input, output)`` arguments of ``test_eval`` function so the latter
|
||||||
|
will be run three times::
|
||||||
|
|
||||||
|
$ py.test -q
|
||||||
|
..F
|
||||||
|
================================= FAILURES =================================
|
||||||
|
____________________________ test_eval[6*9-42] _____________________________
|
||||||
|
|
||||||
|
input = '6*9', expected = 42
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(("input", "expected"), [
|
||||||
|
("3+5", 8),
|
||||||
|
("2+4", 6),
|
||||||
|
("6*9", 42),
|
||||||
|
])
|
||||||
|
def test_eval(input, expected):
|
||||||
|
> assert eval(input) == expected
|
||||||
|
E assert 54 == 42
|
||||||
|
E + where 54 = eval('6*9')
|
||||||
|
|
||||||
|
test_expectation.py:8: AssertionError
|
||||||
|
|
||||||
|
As expected only one pair of input/output values fails the simple test function.
|
||||||
|
As usual you can see the ``input`` and ``output`` values in the traceback.
|
||||||
|
|
||||||
|
Note that there are various ways how you can mark groups of functions,
|
||||||
|
see :ref:`mark`.
|
||||||
|
|
||||||
|
|
||||||
|
.. _`pytest_generate_tests`:
|
||||||
|
|
||||||
|
Basic ``pytest_generate_tests`` example
|
||||||
|
---------------------------------------------
|
||||||
|
|
||||||
|
.. XXX
|
||||||
|
|
||||||
|
> line 598 "Basic ``pytest_generate_tests`` example" - I think this is
|
||||||
|
> not a very basic example! I think it is copied from parametrize.txt
|
||||||
|
> page, where it might make more sense. Here is what I would consider a
|
||||||
|
> basic example.
|
||||||
|
>
|
||||||
|
> # code
|
||||||
|
> def isSquare(n):
|
||||||
|
> n = n ** 0.5
|
||||||
|
> return int(n) == n
|
||||||
|
>
|
||||||
|
> # test file
|
||||||
|
> def pytest_generate_tests(metafunc):
|
||||||
|
> squares = [1, 4, 9, 16, 25, 36, 49]
|
||||||
|
> for n in range(1, 50):
|
||||||
|
> expected = n in squares
|
||||||
|
> if metafunc.function.__name__ == 'test_isSquare':
|
||||||
|
> metafunc.addcall(id=n, funcargs=dict(n=n,
|
||||||
|
> expected=expected))
|
||||||
|
>
|
||||||
|
>
|
||||||
|
> def test_isSquare(n, expected):
|
||||||
|
> assert isSquare(n) == expected
|
||||||
|
|
||||||
|
|
||||||
|
.. XXX
|
||||||
|
consider adding more examples, also mixed (factory-parametrized/test-function-parametrized, see mail from Brianna)
|
||||||
|
|
||||||
|
The ``pytest_generate_tests`` hook is typically used if you want
|
||||||
|
to go beyond what ``@pytest.mark.parametrize`` offers. For example,
|
||||||
|
let's say we want to execute a test with different computation
|
||||||
|
parameters and the parameter range shall be determined by a command
|
||||||
|
line argument. Let's first write a simple (do-nothing) computation test::
|
||||||
|
|
||||||
|
# content of test_compute.py
|
||||||
|
|
||||||
|
def test_compute(param1):
|
||||||
|
assert param1 < 4
|
||||||
|
|
||||||
|
Now we add a ``conftest.py`` file containing the addition of a
|
||||||
|
command line option and the generation of tests depending on
|
||||||
|
that option::
|
||||||
|
|
||||||
|
# content of conftest.py
|
||||||
|
|
||||||
|
def pytest_addoption(parser):
|
||||||
|
parser.addoption("--all", action="store_true",
|
||||||
|
help="run all combinations")
|
||||||
|
|
||||||
|
def pytest_generate_tests(metafunc):
|
||||||
|
if 'param1' in metafunc.fixturenames:
|
||||||
|
if metafunc.config.option.all:
|
||||||
|
end = 5
|
||||||
|
else:
|
||||||
|
end = 2
|
||||||
|
metafunc.parametrize("param1", range(end))
|
||||||
|
|
||||||
|
This means that we only run two tests if no option is passed::
|
||||||
|
|
||||||
|
$ py.test -q test_compute.py
|
||||||
|
..
|
||||||
|
|
||||||
|
And we run five tests if we add the ``--all`` option::
|
||||||
|
|
||||||
|
$ py.test -q --all test_compute.py
|
||||||
|
....F
|
||||||
|
================================= FAILURES =================================
|
||||||
|
_____________________________ test_compute[4] ______________________________
|
||||||
|
|
||||||
|
param1 = 4
|
||||||
|
|
||||||
|
def test_compute(param1):
|
||||||
|
> assert param1 < 4
|
||||||
|
E assert 4 < 4
|
||||||
|
|
||||||
|
test_compute.py:3: AssertionError
|
||||||
|
|
||||||
|
As expected when running the full range of ``param1`` values
|
||||||
|
we'll get an error on the last one.
|
||||||
|
|
||||||
|
You might want to look at :ref:`more parametrization examples <paramexamples>`.
|
||||||
|
|
||||||
|
|
||||||
|
.. _`metafunc object`:
|
||||||
|
|
||||||
|
The **metafunc** object
|
||||||
|
-------------------------------------------
|
||||||
|
|
||||||
|
.. currentmodule:: _pytest.python
|
||||||
|
|
||||||
|
metafunc objects are passed to the ``pytest_generate_tests`` hook.
|
||||||
|
They help to inspect a testfunction and to generate tests
|
||||||
|
according to test configuration or values specified
|
||||||
|
in the class or module where a test function is defined:
|
||||||
|
|
||||||
|
``metafunc.fixturenames``: set of required function arguments for given function
|
||||||
|
|
||||||
|
``metafunc.function``: underlying python test function
|
||||||
|
|
||||||
|
``metafunc.cls``: class object where the test function is defined in or None.
|
||||||
|
|
||||||
|
``metafunc.module``: the module object where the test function is defined in.
|
||||||
|
|
||||||
|
``metafunc.config``: access to command line opts and general config
|
||||||
|
|
||||||
|
``metafunc.funcargnames``: alias for ``fixturenames``, for pre-2.3 compatibility
|
||||||
|
|
||||||
|
.. automethod:: Metafunc.parametrize
|
||||||
|
.. automethod:: Metafunc.addcall(funcargs=None,id=_notexists,param=_notexists)
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
|
|
||||||
Page has moved to fixture
|
setup: is now an "autoactive fixture"
|
||||||
========================================================
|
========================================================
|
||||||
|
|
||||||
During development prior to the pytest-2.3 release the name
|
During development prior to the pytest-2.3 release the name
|
||||||
``pytest.setup`` was used but before the release it was renamed
|
``pytest.setup`` was used but before the release it was renamed
|
||||||
to :ref:`pytest.fixture` mainly to avoid the misconception that there
|
and moved to become part of the general fixture mechanism,
|
||||||
should be a ``pytest.teardown`` as well.
|
namely :ref:`autoactive fixtures`
|
||||||
|
|
||||||
Please refer to :ref:`pytest.fixture` for information on the new
|
|
||||||
fixture functions.
|
|
||||||
|
|
||||||
|
|
|
@ -130,9 +130,8 @@ Running it with the report-on-xfail option gives this output::
|
||||||
|
|
||||||
example $ py.test -rx xfail_demo.py
|
example $ py.test -rx xfail_demo.py
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev2
|
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19
|
||||||
plugins: xdist, bugzilla, cache, oejskit, pep8, cov
|
collected 6 items
|
||||||
collecting ... collected 6 items
|
|
||||||
|
|
||||||
xfail_demo.py xxxxxx
|
xfail_demo.py xxxxxx
|
||||||
========================= short test summary info ==========================
|
========================= short test summary info ==========================
|
||||||
|
@ -148,7 +147,7 @@ Running it with the report-on-xfail option gives this output::
|
||||||
XFAIL xfail_demo.py::test_hello6
|
XFAIL xfail_demo.py::test_hello6
|
||||||
reason: reason
|
reason: reason
|
||||||
|
|
||||||
======================== 6 xfailed in 0.04 seconds =========================
|
======================== 6 xfailed in 0.03 seconds =========================
|
||||||
|
|
||||||
.. _`evaluation of skipif/xfail conditions`:
|
.. _`evaluation of skipif/xfail conditions`:
|
||||||
|
|
||||||
|
|
|
@ -18,12 +18,11 @@ Basic usage and funcargs:
|
||||||
- `pycon 2010 tutorial PDF`_ and `tutorial1 repository`_
|
- `pycon 2010 tutorial PDF`_ and `tutorial1 repository`_
|
||||||
|
|
||||||
|
|
||||||
Function arguments:
|
Fixtures and Function arguments:
|
||||||
|
|
||||||
- :ref:`mysetup`
|
- :ref:`fixtures`
|
||||||
- `application setup in test functions with funcargs`_
|
|
||||||
- `monkey patching done right`_ (blog post, consult `monkeypatch
|
- `monkey patching done right`_ (blog post, consult `monkeypatch
|
||||||
plugin`_ for actual 1.0 API)
|
plugin`_ for up-to-date API)
|
||||||
|
|
||||||
Test parametrization:
|
Test parametrization:
|
||||||
|
|
||||||
|
|
|
@ -28,16 +28,15 @@ Running this would result in a passed test except for the last
|
||||||
|
|
||||||
$ py.test test_tmpdir.py
|
$ py.test test_tmpdir.py
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev2
|
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19
|
||||||
plugins: xdist, bugzilla, cache, oejskit, pep8, cov
|
collected 1 items
|
||||||
collecting ... collected 1 items
|
|
||||||
|
|
||||||
test_tmpdir.py F
|
test_tmpdir.py F
|
||||||
|
|
||||||
================================= FAILURES =================================
|
================================= FAILURES =================================
|
||||||
_____________________________ test_create_file _____________________________
|
_____________________________ test_create_file _____________________________
|
||||||
|
|
||||||
tmpdir = local('/home/hpk/tmp/pytest-2886/test_create_file0')
|
tmpdir = local('/tmp/pytest-169/test_create_file0')
|
||||||
|
|
||||||
def test_create_file(tmpdir):
|
def test_create_file(tmpdir):
|
||||||
p = tmpdir.mkdir("sub").join("hello.txt")
|
p = tmpdir.mkdir("sub").join("hello.txt")
|
||||||
|
@ -48,7 +47,7 @@ Running this would result in a passed test except for the last
|
||||||
E assert 0
|
E assert 0
|
||||||
|
|
||||||
test_tmpdir.py:7: AssertionError
|
test_tmpdir.py:7: AssertionError
|
||||||
========================= 1 failed in 0.23 seconds =========================
|
========================= 1 failed in 0.02 seconds =========================
|
||||||
|
|
||||||
.. _`base temporary directory`:
|
.. _`base temporary directory`:
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
|
|
||||||
.. _`unittest.TestCase`:
|
.. _`unittest.TestCase`:
|
||||||
|
|
||||||
Support for unittest.TestCase
|
Support for unittest.TestCase / Integration of fixtures
|
||||||
=====================================================================
|
=====================================================================
|
||||||
|
|
||||||
py.test has limited support for running Python `unittest.py style`_ tests.
|
py.test has limited support for running Python `unittest.py style`_ tests.
|
||||||
|
@ -24,9 +24,8 @@ Running it yields::
|
||||||
|
|
||||||
$ py.test test_unittest.py
|
$ py.test test_unittest.py
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev12
|
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19
|
||||||
plugins: xdist, bugzilla, cache, oejskit, cli, timeout, pep8, cov
|
collected 1 items
|
||||||
collecting ... collected 1 items
|
|
||||||
|
|
||||||
test_unittest.py F
|
test_unittest.py F
|
||||||
|
|
||||||
|
@ -43,20 +42,20 @@ Running it yields::
|
||||||
test_unittest.py:8: AssertionError
|
test_unittest.py:8: AssertionError
|
||||||
----------------------------- Captured stdout ------------------------------
|
----------------------------- Captured stdout ------------------------------
|
||||||
hello
|
hello
|
||||||
========================= 1 failed in 0.03 seconds =========================
|
========================= 1 failed in 0.01 seconds =========================
|
||||||
|
|
||||||
.. _`unittest.py style`: http://docs.python.org/library/unittest.html
|
.. _`unittest.py style`: http://docs.python.org/library/unittest.html
|
||||||
|
|
||||||
Moreover, you can use the new :ref:`@pytest.setup functions <@pytest.setup>`
|
Moreover, you can use pytest's new :ref:`autoactive fixtures`
|
||||||
functions and make use of pytest's unique :ref:`funcarg mechanism` in your
|
functions, thereby connecting pytest's :ref:`fixture mechanism <fixture>`
|
||||||
test suite::
|
with a setup/teardown style::
|
||||||
|
|
||||||
# content of test_unittest_funcargs.py
|
# content of test_unittest_funcargs.py
|
||||||
import pytest
|
import pytest
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
class MyTest(unittest.TestCase):
|
class MyTest(unittest.TestCase):
|
||||||
@pytest.setup()
|
@pytest.fixture(autoactive=True)
|
||||||
def chdir(self, tmpdir):
|
def chdir(self, tmpdir):
|
||||||
tmpdir.chdir() # change to pytest-provided temporary directory
|
tmpdir.chdir() # change to pytest-provided temporary directory
|
||||||
tmpdir.join("samplefile.ini").write("# testdata")
|
tmpdir.join("samplefile.ini").write("# testdata")
|
||||||
|
@ -70,41 +69,66 @@ function took care to prepare a directory with some test data
|
||||||
which the unittest-testcase method can now use::
|
which the unittest-testcase method can now use::
|
||||||
|
|
||||||
$ py.test -q test_unittest_funcargs.py
|
$ py.test -q test_unittest_funcargs.py
|
||||||
collecting ... collected 1 items
|
|
||||||
.
|
.
|
||||||
1 passed in 0.28 seconds
|
|
||||||
|
|
||||||
If you want to make a database attribute available on unittest.TestCases
|
If you want to make a database attribute available on unittest.TestCases
|
||||||
instances, based on a marker, you can do it using :ref:`pytest.mark`` and
|
instances, you can do it using :ref:`usefixtures` and a simple
|
||||||
:ref:`setup functions`::
|
:ref:`fixture function`::
|
||||||
|
|
||||||
# content of test_unittest_marked_db.py
|
# content of test_unittest_marked_db.py
|
||||||
import pytest
|
import pytest
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
@pytest.fixture()
|
@pytest.fixture
|
||||||
def db():
|
def db(request):
|
||||||
class DummyDB:
|
class DummyDB:
|
||||||
x = 1
|
entries = []
|
||||||
return DummyDB()
|
db = DummyDB()
|
||||||
|
if request.instance is not None:
|
||||||
@pytest.setup()
|
|
||||||
def stick_db_to_self(request, db):
|
|
||||||
if hasattr(request.node.markers, "needsdb"):
|
|
||||||
request.instance.db = db
|
request.instance.db = db
|
||||||
|
return db
|
||||||
|
|
||||||
|
@pytest.mark.usefixtures("db")
|
||||||
class MyTest(unittest.TestCase):
|
class MyTest(unittest.TestCase):
|
||||||
def test_method(self):
|
def test_append(self):
|
||||||
assert not hasattr(self, "db")
|
self.db.entries.append(1)
|
||||||
|
|
||||||
@pytest.mark.needsdb
|
|
||||||
def test_method2(self):
|
def test_method2(self):
|
||||||
assert self.db.x == 1
|
# check we have a fresh instance
|
||||||
|
assert len(self.db.entries) == 0
|
||||||
|
|
||||||
Running it passes both tests, one of which will see a ``db`` attribute
|
Running it passes both tests::
|
||||||
because of the according ``needsdb`` marker::
|
|
||||||
|
|
||||||
$ py.test -q test_unittest_marked_db.py
|
$ py.test -q test_unittest_marked_db.py
|
||||||
collecting ... collected 2 items
|
|
||||||
..
|
..
|
||||||
2 passed in 0.03 seconds
|
|
||||||
|
If you rather want to provide a class-cached "db" attribute, you
|
||||||
|
can write a slightly different fixture using a ``scope`` parameter
|
||||||
|
for the fixture decorator ::
|
||||||
|
|
||||||
|
# content of test_unittest_class_db.py
|
||||||
|
import pytest
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
@pytest.fixture(scope="class")
|
||||||
|
def db_class(request):
|
||||||
|
class DummyDB:
|
||||||
|
entries = []
|
||||||
|
db = DummyDB()
|
||||||
|
if request.cls is not None:
|
||||||
|
request.cls.db = db
|
||||||
|
return db
|
||||||
|
|
||||||
|
@pytest.mark.usefixtures("db_class")
|
||||||
|
class MyTest(unittest.TestCase):
|
||||||
|
def test_append(self):
|
||||||
|
self.db.entries.append(1)
|
||||||
|
|
||||||
|
def test_method2(self):
|
||||||
|
# check we DONT have a fresh instance
|
||||||
|
assert len(self.db.entries) == 1
|
||||||
|
|
||||||
|
Running it again passes both tests::
|
||||||
|
|
||||||
|
$ py.test -q test_unittest_class_db.py
|
||||||
|
..
|
||||||
|
|
|
@ -183,9 +183,7 @@ Running it will show that ``MyPlugin`` was added and its
|
||||||
hook was invoked::
|
hook was invoked::
|
||||||
|
|
||||||
$ python myinvoke.py
|
$ python myinvoke.py
|
||||||
collecting ... collected 0 items
|
|
||||||
|
|
||||||
in 0.01 seconds
|
|
||||||
*** test run reporting finishing
|
*** test run reporting finishing
|
||||||
|
|
||||||
.. include:: links.inc
|
.. include:: links.inc
|
||||||
|
|
|
@ -1,16 +1,17 @@
|
||||||
|
|
||||||
.. _`classic xunit`:
|
.. _`classic xunit`:
|
||||||
|
.. _xunitsetup:
|
||||||
|
|
||||||
classic xunit-style setup
|
classic xunit-style setup
|
||||||
========================================
|
========================================
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
This section describes the old way how you can implement setup and
|
This section describes the classic way how you can implement setup and
|
||||||
teardown on a per-module/class/function basis. It remains fully
|
teardown on a per-module/class/function basis. It remains fully
|
||||||
supported but it is recommended to rather use :ref:`fixture functions
|
supported but it is recommended to rather use the more flexible,
|
||||||
<fixture>` or :ref:`funcargs <resources>` for implementing your
|
more modular and more scalable :ref:`fixture functions
|
||||||
needs to prepare and fix the test state for your tests.
|
<fixture>` for implementing for fixing test state for your tests.
|
||||||
|
|
||||||
Module level setup/teardown
|
Module level setup/teardown
|
||||||
--------------------------------------
|
--------------------------------------
|
||||||
|
|
11
tox.ini
11
tox.ini
|
@ -43,13 +43,22 @@ deps=
|
||||||
|
|
||||||
[testenv:doc]
|
[testenv:doc]
|
||||||
basepython=python
|
basepython=python
|
||||||
changedir=doc
|
changedir=doc/en
|
||||||
deps=:pypi:sphinx
|
deps=:pypi:sphinx
|
||||||
pytest
|
pytest
|
||||||
|
|
||||||
commands=
|
commands=
|
||||||
|
make clean
|
||||||
make html
|
make html
|
||||||
|
|
||||||
|
[testenv:regen]
|
||||||
|
basepython=python
|
||||||
|
changedir=doc/en
|
||||||
|
deps=:pypi:sphinx
|
||||||
|
pytest
|
||||||
|
commands=
|
||||||
|
make regen
|
||||||
|
|
||||||
[testenv:py31]
|
[testenv:py31]
|
||||||
deps=:pypi:nose>=1.0
|
deps=:pypi:nose>=1.0
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue