5 new commits in pytest: https://bitbucket.org/hpk42/pytest/commits/8dfa43a47cca/ Changeset: 8dfa43a47cca User: hpk42 Date: 2013-05-07 16:26:56 Summary: don't use indexservers anymore Affected #: 1 file
diff -r 37ebb8278004f6aa99f4deafe032c790c5cc5e99 -r 8dfa43a47ccac80dbf252d35c2156ae9e0d38637 tox.ini --- a/tox.ini +++ b/tox.ini @@ -1,17 +1,13 @@ [tox] distshare={homedir}/.tox/distshare envlist=py25,py26,py27,py27-nobyte,py32,py33,py27-xdist,trial -indexserver= - pypi = https://pypi.python.org/simple - testrun = http://pypi.testrun.org - default = http://pypi.testrun.org [testenv] changedir=testing commands= py.test --lsof -rfsxX --junitxml={envlogdir}/junit-{envname}.xml [] deps= - :pypi:pexpect - :pypi:nose + pexpect + nose [testenv:genscript] changedir=. @@ -21,8 +17,8 @@ changedir=. basepython=python2.7 deps=pytest-xdist - :pypi:mock - :pypi:nose + mock + nose commands= py.test -n3 -rfsxX \ --junitxml={envlogdir}/junit-{envname}.xml testing @@ -39,8 +35,8 @@ [testenv:trial] changedir=. -deps=:pypi:twisted - :pypi:pexpect +deps=twisted + pexpect commands= py.test -rsxf testing/test_unittest.py \ --junitxml={envlogdir}/junit-{envname}.xml {posargs:testing/test_unittest.py} @@ -51,17 +47,17 @@ [testenv:py32] deps= - :pypi:nose + nose [testenv:py33] deps= - :pypi:nose + nose [testenv:doc] basepython=python changedir=doc/en -deps=:pypi:sphinx - :pypi:PyYAML +deps=sphinx + PyYAML commands= make clean @@ -70,15 +66,15 @@ [testenv:regen] basepython=python changedir=doc/en -deps=:pypi:sphinx - :pypi:PyYAML +deps=sphinx + PyYAML commands= rm -rf /tmp/doc-exec* #pip install pytest==2.3.4 make regen [testenv:py31] -deps=:pypi:nose>=1.0 +deps=nose>=1.0 [testenv:py31-xdist] deps=pytest-xdist https://bitbucket.org/hpk42/pytest/commits/35e73638a6e4/ Changeset: 35e73638a6e4 User: hpk42 Date: 2013-05-07 18:40:26 Summary: support boolean condition expressions in skipif/xfail change documentation to prefer it over string expressions Affected #: 6 files diff -r 8dfa43a47ccac80dbf252d35c2156ae9e0d38637 -r 35e73638a6e4a1ae8b9b14237da4469960085d05 CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -1,9 +1,15 @@ -Changes between 2.3.5 and DEV +Changes between 2.3.5 and 2.4.DEV ----------------------------------- - (experimental) allow fixture functions to be implemented as context managers. Thanks Andreas Pelme, - ladimir Keleshev. + Vladimir Keleshev. + +- (experimental) allow boolean expression directly with skipif/xfail + if a "reason" is also specified. Rework skipping documentation + to recommend "condition as booleans" because it prevents surprises + when importing markers between modules. Specifying conditions + as strings will remain fully supported. - fix issue245 by depending on the released py-1.4.14 which fixes py.io.dupfile to work with files with no diff -r 8dfa43a47ccac80dbf252d35c2156ae9e0d38637 -r 35e73638a6e4a1ae8b9b14237da4469960085d05 _pytest/__init__.py --- a/_pytest/__init__.py +++ b/_pytest/__init__.py @@ -1,2 +1,2 @@ # -__version__ = '2.3.6.dev3' +__version__ = '2.4.0.dev1' diff -r 8dfa43a47ccac80dbf252d35c2156ae9e0d38637 -r 35e73638a6e4a1ae8b9b14237da4469960085d05 _pytest/skipping.py --- a/_pytest/skipping.py +++ b/_pytest/skipping.py @@ -89,7 +89,11 @@ if isinstance(expr, py.builtin._basestring): result = cached_eval(self.item.config, expr, d) else: - pytest.fail("expression is not a string") + if self.get("reason") is None: + # XXX better be checked at collection time + pytest.fail("you need to specify reason=STRING " + "when using booleans as conditions.") + result = bool(expr) if result: self.result = True self.expr = expr diff -r 8dfa43a47ccac80dbf252d35c2156ae9e0d38637 -r 35e73638a6e4a1ae8b9b14237da4469960085d05 doc/en/skipping.txt --- a/doc/en/skipping.txt +++ b/doc/en/skipping.txt @@ -9,86 +9,110 @@ or that you expect to fail you can mark them accordingly or you may call helper functions during execution of setup or test functions. -A *skip* means that you expect your test to pass unless a certain -configuration or condition (e.g. wrong Python interpreter, missing -dependency) prevents it to run. And *xfail* means that your test -can run but you expect it to fail because there is an implementation problem. +A *skip* means that you expect your test to pass unless the environment +(e.g. wrong Python interpreter, missing dependency) prevents it to run. +And *xfail* means that your test can run but you expect it to fail +because there is an implementation problem. -py.test counts and lists *skip* and *xfail* tests separately. However, -detailed information about skipped/xfailed tests is not shown by default -to avoid cluttering the output. You can use the ``-r`` option to see -details corresponding to the "short" letters shown in the test -progress:: +py.test counts and lists *skip* and *xfail* tests separately. Detailed +information about skipped/xfailed tests is not shown by default to avoid +cluttering the output. You can use the ``-r`` option to see details +corresponding to the "short" letters shown in the test progress:: py.test -rxs # show extra info on skips and xfails (See :ref:`how to change command line options defaults`) .. _skipif: +.. _`condition booleans`: Marking a test function to be skipped ------------------------------------------- +.. versionadded:: 2.4 + Here is an example of marking a test function to be skipped -when run on a Python3 interpreter:: +when run on a Python3.3 interpreter:: import sys - @pytest.mark.skipif("sys.version_info >= (3,0)") + @pytest.mark.skipif(sys.version_info >= (3,3), + reason="requires python3.3") def test_function(): ... -During test function setup the skipif condition is -evaluated by calling ``eval('sys.version_info >= (3,0)', namespace)``. -(*New in version 2.0.2*) The namespace contains all the module globals of the test function so that -you can for example check for versions of a module you are using:: +During test function setup the condition ("sys.version_info >= (3,3)") is +checked. If it evaluates to True, the test function will be skipped +with the specified reason. Note that pytest enforces specifying a reason +in order to report meaningful "skip reasons" (e.g. when using ``-rs``). + +You can share skipif markers between modules. Consider this test module:: + + # content of test_mymodule.py import mymodule - - @pytest.mark.skipif("mymodule.__version__ < '1.2'") - def test_function(): - ... - -The test function will not be run ("skipped") if -``mymodule`` is below the specified version. The reason -for specifying the condition as a string is mainly that -py.test can report a summary of skip conditions. -For information on the construction of the ``namespace`` -see `evaluation of skipif/xfail conditions`_. - -You can of course create a shortcut for your conditional skip -decorator at module level like this:: - - win32only = pytest.mark.skipif("sys.platform != 'win32'") - - @win32only + minversion = pytest.mark.skipif(mymodule.__versioninfo__ >= (1,1), + reason="at least mymodule-1.1 required") + @minversion def test_function(): ... -Skip all test functions of a class --------------------------------------- +You can import it from another test module:: + + # test_myothermodule.py + from test_mymodule import minversion + + @minversion + def test_anotherfunction(): + ... + +For larger test suites it's usually a good idea to have one file +where you define the markers which you then consistently apply +throughout your test suite. + +Alternatively, the pre pytest-2.4 way to specify `condition strings <condition strings>`_ instead of booleans will remain fully supported in future +versions of pytest. It couldn't be easily used for importing markers +between test modules so it's no longer advertised as the primary method. + + +Skip all test functions of a class or module +--------------------------------------------- As with all function :ref:`marking <mark>` you can skip test functions at the -`whole class- or module level`_. Here is an example -for skipping all methods of a test class based on the platform:: +`whole class- or module level`_. If your code targets python2.6 or above you +use the skipif decorator (and any other marker) on classes:: - class TestPosixCalls: - pytestmark = pytest.mark.skipif("sys.platform == 'win32'") - - def test_function(self): - "will not be setup or run under 'win32' platform" - -The ``pytestmark`` special name tells py.test to apply it to each test -function in the class. If your code targets python2.6 or above you can -more naturally use the skipif decorator (and any other marker) on -classes:: - - @pytest.mark.skipif("sys.platform == 'win32'") + @pytest.mark.skipif(sys.platform == 'win32', + reason="requires windows") class TestPosixCalls: def test_function(self): "will not be setup or run under 'win32' platform" -Using multiple "skipif" decorators on a single function is generally fine - it means that if any of the conditions apply the function execution will be skipped. +If the condition is true, this marker will produce a skip result for +each of the test methods. + +If your code targets python2.5 where class-decorators are not available, +you can set the ``pytestmark`` attribute of a class:: + + class TestPosixCalls: + pytestmark = pytest.mark.skipif(sys.platform == 'win32', + reason="requires Windows") + + def test_function(self): + "will not be setup or run under 'win32' platform" + +As with the class-decorator, the ``pytestmark`` special name tells +py.test to apply it to each test function in the class. + +If you want to skip all test functions of a module, you must use +the ``pytestmark`` name on the global level:: + + # test_module.py + + pytestmark = pytest.mark.skipif(...) + +If multiple "skipif" decorators are applied to a test function, it +will be skipped if any of the skip conditions is true. .. _`whole class- or module level`: mark.html#scoped-marking @@ -118,7 +142,8 @@ As with skipif_ you can also mark your expectation of a failure on a particular platform:: - @pytest.mark.xfail("sys.version_info >= (3,0)") + @pytest.mark.xfail(sys.version_info >= (3,3), + reason="python3.3 api changes") def test_function(): ... @@ -151,41 +176,19 @@ ======================== 6 xfailed in 0.05 seconds ========================= -.. _`evaluation of skipif/xfail conditions`: - -Evaluation of skipif/xfail expressions ----------------------------------------------------- - -.. versionadded:: 2.0.2 - -The evaluation of a condition string in ``pytest.mark.skipif(conditionstring)`` -or ``pytest.mark.xfail(conditionstring)`` takes place in a namespace -dictionary which is constructed as follows: - -* the namespace is initialized by putting the ``sys`` and ``os`` modules - and the pytest ``config`` object into it. - -* updated with the module globals of the test function for which the - expression is applied. - -The pytest ``config`` object allows you to skip based on a test configuration value -which you might have added:: - - @pytest.mark.skipif("not config.getvalue('db')") - def test_function(...): - ... - Imperative xfail from within a test or setup function ------------------------------------------------------ -If you cannot declare xfail-conditions at import time -you can also imperatively produce an XFail-outcome from -within test or setup code. Example:: +If you cannot declare xfail- of skipif conditions at import +time you can also imperatively produce an according outcome +imperatively, in test or setup code:: def test_function(): if not valid_config(): - pytest.xfail("unsupported configuration") + pytest.xfail("failing configuration (but should work)") + # or + pytest.skipif("unsupported configuration") Skipping on a missing import dependency @@ -202,16 +205,61 @@ docutils = pytest.importorskip("docutils", minversion="0.3") -The version will be read from the specified module's ``__version__`` attribute. +The version will be read from the specified +module's ``__version__`` attribute. -Imperative skip from within a test or setup function ------------------------------------------------------- -If for some reason you cannot declare skip-conditions -you can also imperatively produce a skip-outcome from -within test or setup code. Example:: +.. _`string conditions`: +specifying conditions as strings versus booleans +---------------------------------------------------------- + +Prior to pytest-2.4 the only way to specify skipif/xfail conditions was +to use strings:: + + import sys + @pytest.mark.skipif("sys.version_info >= (3,3)") def test_function(): - if not valid_config(): - pytest.skip("unsupported configuration") + ... +During test function setup the skipif condition is evaluated by calling +``eval('sys.version_info >= (3,0)', namespace)``. The namespace contains +all the module globals, and ``os`` and ``sys`` as a minimum. + +Since pytest-2.4 `condition booleans`_ are considered preferable +because markers can then be freely imported between test modules. +With strings you need to import not only the marker but all variables +everything used by the marker, which violates encapsulation. + +The reason for specifying the condition as a string was that py.test can +report a summary of skip conditions based purely on the condition string. +With conditions as booleans you are required to specify a ``reason`` string. + +Note that string conditions will remain fully supported and you are free +to use them if you have no need for cross-importing markers. + +The evaluation of a condition string in ``pytest.mark.skipif(conditionstring)`` +or ``pytest.mark.xfail(conditionstring)`` takes place in a namespace +dictionary which is constructed as follows: + +* the namespace is initialized by putting the ``sys`` and ``os`` modules + and the pytest ``config`` object into it. + +* updated with the module globals of the test function for which the + expression is applied. + +The pytest ``config`` object allows you to skip based on a test +configuration value which you might have added:: + + @pytest.mark.skipif("not config.getvalue('db')") + def test_function(...): + ... + +The equivalent with "boolean conditions" is:: + + @pytest.mark.skipif(not pytest.config.getvalue("db"), + reason="--db was not specified") + def test_function(...): + pass + + diff -r 8dfa43a47ccac80dbf252d35c2156ae9e0d38637 -r 35e73638a6e4a1ae8b9b14237da4469960085d05 setup.py --- a/setup.py +++ b/setup.py @@ -12,7 +12,7 @@ name='pytest', description='py.test: simple powerful testing with Python', long_description = long_description, - version='2.3.6.dev3', + version='2.4.0.dev1', url='http://pytest.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], diff -r 8dfa43a47ccac80dbf252d35c2156ae9e0d38637 -r 35e73638a6e4a1ae8b9b14237da4469960085d05 testing/test_skipping.py --- a/testing/test_skipping.py +++ b/testing/test_skipping.py @@ -569,7 +569,6 @@ "*xfail(*condition, reason=None, run=True)*expected failure*", ]) - def test_xfail_test_setup_exception(testdir): testdir.makeconftest(""" def pytest_runtest_setup(): @@ -610,3 +609,44 @@ """) +class TestBooleanCondition: + def test_skipif(self, testdir): + testdir.makepyfile(""" + import pytest + @pytest.mark.skipif(True, reason="True123") + def test_func1(): + pass + @pytest.mark.skipif(False, reason="True123") + def test_func2(): + pass + """) + result = testdir.runpytest() + result.stdout.fnmatch_lines(""" + *1 passed*1 skipped* + """) + + def test_skipif_noreason(self, testdir): + testdir.makepyfile(""" + import pytest + @pytest.mark.skipif(True) + def test_func(): + pass + """) + result = testdir.runpytest("-rs") + result.stdout.fnmatch_lines(""" + *1 error* + """) + + def test_xfail(self, testdir): + testdir.makepyfile(""" + import pytest + @pytest.mark.xfail(True, reason="True123") + def test_func(): + assert 0 + """) + result = testdir.runpytest("-rxs") + result.stdout.fnmatch_lines(""" + *XFAIL* + *True123* + *1 xfail* + """) https://bitbucket.org/hpk42/pytest/commits/cccbdc95b3fa/ Changeset: cccbdc95b3fa User: hpk42 Date: 2013-05-07 21:34:59 Summary: enhance index page, fix announcement index Affected #: 3 files diff -r 35e73638a6e4a1ae8b9b14237da4469960085d05 -r cccbdc95b3fa4b1a111449fdcef7319b34502e76 doc/en/announce/index.txt --- a/doc/en/announce/index.txt +++ b/doc/en/announce/index.txt @@ -5,6 +5,7 @@ .. toctree:: :maxdepth: 2 + release-2.3.5 release-2.3.4 release-2.3.3 release-2.3.2 diff -r 35e73638a6e4a1ae8b9b14237da4469960085d05 -r cccbdc95b3fa4b1a111449fdcef7319b34502e76 doc/en/index.txt --- a/doc/en/index.txt +++ b/doc/en/index.txt @@ -11,8 +11,11 @@ - runs on Posix/Windows, Python 2.4-3.3, PyPy and Jython-2.5.1 - :ref:`comprehensive online <toc>` and `PDF documentation <pytest.pdf>`_ + - many :ref:`third party plugins <extplugins>` and + :ref:`builtin helpers <pytest helpers>` - used in :ref:`many projects and organisations <projects>`, in test - suites ranging from 10 to 10s of thousands of tests + suites with up to twenty thousand tests + - strict policy of remaining backward compatible across releases - comes with many :ref:`tested examples <examples>` **provides easy no-boilerplate testing** @@ -26,13 +29,13 @@ **scales from simple unit to complex functional testing** - - (new in 2.3) :ref:`modular parametrizeable fixtures <fixture>` + - :ref:`modular parametrizeable fixtures <fixture>` (new in 2.3, + improved in 2.4) - :ref:`parametrized test functions <parametrized test functions>` - :ref:`mark` - - :ref:`skipping` + - :ref:`skipping` (improved in 2.4) - can :ref:`distribute tests to multiple CPUs <xdistcpu>` through :ref:`xdist plugin <xdist>` - can :ref:`continuously re-run failing tests <looponfailing>` - - many :ref:`builtin helpers <pytest helpers>` and :ref:`plugins <plugins>` - flexible :ref:`Python test discovery` **integrates many common testing methods**: @@ -50,8 +53,8 @@ **extensive plugin and customization system**: - all collection, reporting, running aspects are delegated to hook functions - - customizations can be per-directory, per-project or per PyPI released plugins - - it is easy to add command line options or do other kind of add-ons and customizations. + - customizations can be per-directory, per-project or per PyPI released plugin + - it is easy to add command line options or customize existing behaviour .. _`Javascript unit- and functional testing`: http://pypi.python.org/pypi/oejskit diff -r 35e73638a6e4a1ae8b9b14237da4469960085d05 -r cccbdc95b3fa4b1a111449fdcef7319b34502e76 doc/en/plugins.txt --- a/doc/en/plugins.txt +++ b/doc/en/plugins.txt @@ -78,12 +78,22 @@ * `pytest-capturelog <http://pypi.python.org/pypi/pytest-capturelog>`_: to capture and assert about messages from the logging module +* `pytest-cov <http://pypi.python.org/pypi/pytest-cov>`_: + coverage reporting, compatible with distributed testing + * `pytest-xdist <http://pypi.python.org/pypi/pytest-xdist>`_: to distribute tests to CPUs and remote hosts, to run in boxed mode which allows to survive segmentation faults, to run in looponfailing mode, automatically re-running failing tests on file changes, see also :ref:`xdist` +* `pytest-instafail <http://pypi.python.org/pypi/pytest-instafail>`_: + to report failures while the test run is happening. + +* `pytest-bdd <http://pypi.python.org/pypi/pytest-bdd>`_ and + `pytest-konira <http://pypi.python.org/pypi/pytest-konira>`_ + to write tests using behaviour-driven testing. + * `pytest-timeout <http://pypi.python.org/pypi/pytest-timeout>`_: to timeout tests based on function marks or global definitions. @@ -91,9 +101,6 @@ to interactively re-run failing tests and help other plugins to store test run information across invocations. -* `pytest-cov <http://pypi.python.org/pypi/pytest-cov>`_: - coverage reporting, compatible with distributed testing - * `pytest-pep8 <http://pypi.python.org/pypi/pytest-pep8>`_: a ``--pep8`` option to enable PEP8 compliance checking. https://bitbucket.org/hpk42/pytest/commits/5db7ac12892a/ Changeset: 5db7ac12892a User: hpk42 Date: 2013-05-07 21:37:08 Summary: document context fixtures, also improve plugin docs Affected #: 2 files diff -r cccbdc95b3fa4b1a111449fdcef7319b34502e76 -r 5db7ac12892adca887ce08cbf01f5683dc24a75f CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -3,7 +3,7 @@ - (experimental) allow fixture functions to be implemented as context managers. Thanks Andreas Pelme, - Vladimir Keleshev. + Vladimir Keleshev. - (experimental) allow boolean expression directly with skipif/xfail if a "reason" is also specified. Rework skipping documentation diff -r cccbdc95b3fa4b1a111449fdcef7319b34502e76 -r 5db7ac12892adca887ce08cbf01f5683dc24a75f doc/en/fixture.txt --- a/doc/en/fixture.txt +++ b/doc/en/fixture.txt @@ -7,14 +7,14 @@ .. currentmodule:: _pytest.python -.. versionadded:: 2.0/2.3 +.. versionadded:: 2.0/2.3/2.4 .. _`xUnit`: http://en.wikipedia.org/wiki/XUnit -.. _`general purpose of test fixtures`: http://en.wikipedia.org/wiki/Test_fixture#Software +.. _`purpose of test fixtures`: http://en.wikipedia.org/wiki/Test_fixture#Software .. _`Dependency injection`: http://en.wikipedia.org/wiki/Dependency_injection#Definition -The `general purpose of test fixtures`_ is to provide a fixed baseline -upon which tests can reliably and repeatedly execute. pytest-2.3 fixtures +The `purpose of test fixtures`_ is to provide a fixed baseline +upon which tests can reliably and repeatedly execute. pytest fixtures offer dramatic improvements over the classic xUnit style of setup/teardown functions: @@ -22,8 +22,7 @@ from test functions, modules, classes or whole projects. * fixtures are implemented in a modular manner, as each fixture name - triggers a *fixture function* which can itself easily use other - fixtures. + triggers a *fixture function* which can itself use other fixtures. * fixture management scales from simple unit to complex functional testing, allowing to parametrize fixtures and tests according @@ -129,10 +128,10 @@ When injecting fixtures to test functions, pytest-2.0 introduced the term "funcargs" or "funcarg mechanism" which continues to be present -also in pytest-2.3 docs. It now refers to the specific case of injecting +also in docs today. It now refers to the specific case of injecting fixture values as arguments to test functions. With pytest-2.3 there are -more possibilities to use fixtures but "funcargs" probably will remain -as the main way of dealing with fixtures. +more possibilities to use fixtures but "funcargs" remain as the main way +as they allow to directly state the dependencies of a test function. As the following examples show in more detail, funcargs allow test functions to easily receive and work against specific pre-initialized @@ -154,10 +153,10 @@ :py:func:`@pytest.fixture <_pytest.python.fixture>` invocation to cause the decorated ``smtp`` fixture function to only be invoked once per test module. Multiple test functions in a test module will thus -each receive the same ``smtp`` fixture instance. The next example also -extracts the fixture function into a separate ``conftest.py`` file so -that all tests in test modules in the directory can access the fixture -function:: +each receive the same ``smtp`` fixture instance. The next example puts +the fixture function into a separate ``conftest.py`` file so +that tests from multiple test modules in the directory can +access the fixture function:: # content of conftest.py import pytest @@ -233,24 +232,91 @@ def smtp(...): # the returned fixture value will be shared for # all tests needing it + +.. _`contextfixtures`: +fixture finalization / teardowns +------------------------------------------------------------- + +pytest supports two styles of fixture finalization: + +- (new in pytest-2.4) by writing a contextmanager fixture + generator where a fixture value is "yielded" and the remainder + of the function serves as the teardown code. This integrates + very well with existing context managers. + +- by making a fixture function accept a ``request`` argument + with which it can call ``request.addfinalizer(teardownfunction)`` + to register a teardown callback function. + +Both methods are strictly equivalent from pytest's view and will +remain supported in the future. + +Because a number of people prefer the new contextmanager style +we describe it first:: + + # content of test_ctxfixture.py + + import smtplib + import pytest + + @pytest.fixture(scope="module") + def smtp(): + smtp = smtplib.SMTP("merlinux.eu") + yield smtp # provide the fixture value + print ("teardown smtp") + smtp.close() + +pytest detects that you are using a ``yield`` in your fixture function, +turns it into a generator and: + +a) iterates once into it for producing the value +b) iterates a second time for tearing the fixture down, expecting + a StopIteration (which is produced automatically from the Python + runtime when the generator returns). + +.. note:: + + The teardown will execute independently of the status of test functions. + You do not need to write the teardown code into a ``try-finally`` clause + like you would usually do with ``contextlib.contextmanager`` decorated + functions. + + If the fixture generator yields a second value pytest will report + an error. Yielding cannot be used for parametrization. We'll describe + ways to implement parametrization further below. + +Prior to pytest-2.4 you always needed to register a finalizer by accepting +a ``request`` object into your fixture function and calling +``request.addfinalizer`` with a teardown function:: + + import smtplib + import pytest + + @pytest.fixture(scope="module") + def smtp(request): + smtp = smtplib.SMTP("merlinux.eu") + def fin(): + print ("teardown smtp") + smtp.close() + return smtp # provide the fixture value + +This method of registering a finalizer reads more indirect +than the new contextmanager style syntax because ``fin`` +is a callback function. + + .. _`request-context`: Fixtures can interact with the requesting test context ------------------------------------------------------------- -Fixture functions can themselves use other fixtures by naming -them as an input argument just like test functions do, see -:ref:`interdependent fixtures`. Moreover, pytest -provides a builtin :py:class:`request <FixtureRequest>` object, +pytest provides a builtin :py:class:`request <FixtureRequest>` object, which fixture functions can use to introspect the function, class or module -for which they are invoked or to register finalizing (cleanup) -functions which are called when the last test finished execution. +for which they are invoked. Further extending the previous ``smtp`` fixture example, let's -read an optional server URL from the module namespace and register -a finalizer that closes the smtp connection after the last -test in a module finished execution:: +read an optional server URL from the module namespace:: # content of conftest.py import pytest @@ -260,26 +326,25 @@ def smtp(request): server = getattr(request.module, "smtpserver", "merlinux.eu") smtp = smtplib.SMTP(server) - def fin(): - print ("finalizing %s" % smtp) - smtp.close() - request.addfinalizer(fin) - return smtp + yield smtp # provide the fixture + print ("finalizing %s" % smtp) + smtp.close() -The registered ``fin`` function will be called when the last test -using it has executed:: +The finalizing part after the ``yield smtp`` statement will execute +when the last test using the ``smtp`` fixture has executed:: $ py.test -s -q --tb=no FF finalizing <smtplib.SMTP instance at 0x1e10248> We see that the ``smtp`` instance is finalized after the two -tests using it tests executed. If we had specified ``scope='function'`` -then fixture setup and cleanup would occur around each single test. -Note that either case the test module itself does not need to change! +tests which use it finished executin. If we rather specify +``scope='function'`` then fixture setup and cleanup occurs +around each single test. Note that in either case the test +module itself does not need to change! Let's quickly create another test module that actually sets the -server URL and has a test to verify the fixture picks it up:: +server URL in its module namespace:: # content of test_anothersmtp.py @@ -298,6 +363,10 @@ > assert 0, smtp.helo() E AssertionError: (250, 'mail.python.org') +voila! The ``smtp`` fixture function picked up our mail server name +from the module namespace. + + .. _`fixture-parametrize`: Parametrizing a fixture @@ -323,11 +392,9 @@ params=["merlinux.eu", "mail.python.org"]) def smtp(request): smtp = smtplib.SMTP(request.param) - def fin(): - print ("finalizing %s" % smtp) - smtp.close() - request.addfinalizer(fin) - return smtp + yield smtp + print ("finalizing %s" % smtp) + smtp.close() The main change is the declaration of ``params`` with :py:func:`@pytest.fixture <_pytest.python.fixture>`, a list of values @@ -467,10 +534,8 @@ def modarg(request): param = request.param print "create", param - def fin(): - print "fin", param - request.addfinalizer(fin) - return param + yield param + print ("fin %s" % param) @pytest.fixture(scope="function", params=[1,2]) def otherarg(request): @@ -517,9 +582,9 @@ 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. + .. _`usefixtures`: - using fixtures from classes, modules or projects ---------------------------------------------------------------------- https://bitbucket.org/hpk42/pytest/commits/17f3904c6e8e/ Changeset: 17f3904c6e8e User: hpk42 Date: 2013-05-07 21:39:30 Summary: bump version Affected #: 2 files Diff not available. Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. _______________________________________________ pytest-commit mailing list pytest-commit@python.org http://mail.python.org/mailman/listinfo/pytest-commit