diff --git a/_pytest/python.py b/_pytest/python.py index e5d6ffbf0..efb594207 100644 --- a/_pytest/python.py +++ b/_pytest/python.py @@ -10,12 +10,12 @@ from py._code.code import TerminalRepr import _pytest cutdir = py.path.local(_pytest.__file__).dirpath() -class FactoryMarker: +class FixtureFunctionMarker: def __init__(self, scope, params): self.scope = scope self.params = params def __call__(self, function): - function._pytestfactory = self + function._pytestfixturefunction = self return function class SetupMarker: @@ -26,18 +26,32 @@ class SetupMarker: return function # XXX a test fails when scope="function" how it should be, investigate -def factory(scope=None, params=None): - """ return a decorator to mark functions as resource factories. +def fixture(scope=None, params=None): + """ return a decorator to mark a fixture factory function. - :arg scope: the scope for which this resource is shared, one of + The name of the fixture function can be referenced in a test context + to cause activation ahead of running tests. Test modules or classes + can use the pytest.mark.needsfixtures(fixturename) marker to specify + needed fixtures. Test functions can use fixture names as input arguments + in which case the object returned from the fixture function will be + injected. + + :arg scope: the scope for which this fixture is shared, one of "function", "class", "module", "session". Defaults to "function". :arg params: an optional list of parameters which will cause multiple - invocations of tests depending on the resource. + invocations of the fixture functions and their dependent + tests. """ - return FactoryMarker(scope, params) + return FixtureFunctionMarker(scope, params) def setup(scope="function"): - """ return a decorator to mark functions as setup functions. + """ return a decorator to mark a function as providing a fixture for + a testcontext. A fixture function is executed for each scope and may + receive funcargs which allows it to initialise and provide implicit + test state. A fixture function may receive the "testcontext" object + and register a finalizer via "testcontext.addfinalizer(finalizer)" + which will be called when the last test in the testcontext has + executed. :arg scope: the scope for which the setup function will be active, one of "function", "class", "module", "session". @@ -112,7 +126,7 @@ def pytest_sessionstart(session): def pytest_namespace(): raises.Exception = pytest.fail.Exception return { - 'factory': factory, + 'fixture': fixture, 'setup': setup, 'raises' : raises, 'collect': { @@ -1377,9 +1391,9 @@ class FuncargManager: continue # resource factories either have a pytest_funcarg__ prefix # or are "funcarg" marked - marker = getattr(obj, "_pytestfactory", None) + marker = getattr(obj, "_pytestfixturefunction", None) if marker is not None: - if not isinstance(marker, FactoryMarker): + if not isinstance(marker, FixtureFunctionMarker): # magic globals with __getattr__ # give us something thats wrong for that case continue diff --git a/doc/en/example/costlysetup/conftest.py b/doc/en/example/costlysetup/conftest.py index 33ae48f39..d689c11b2 100644 --- a/doc/en/example/costlysetup/conftest.py +++ b/doc/en/example/costlysetup/conftest.py @@ -1,7 +1,7 @@ import pytest -@pytest.factory("session") +@pytest.fixture("session") def setup(request): setup = CostlySetup() request.addfinalizer(setup.finalize) diff --git a/doc/en/example/multipython.py b/doc/en/example/multipython.py index 13f442354..142ccc7ee 100644 --- a/doc/en/example/multipython.py +++ b/doc/en/example/multipython.py @@ -5,12 +5,12 @@ serialization via the pickle module. import py, pytest pythonlist = ['python2.4', 'python2.5', 'python2.6', 'python2.7', 'python2.8'] -@pytest.factory(params=pythonlist) +@pytest.fixture(params=pythonlist) def python1(request, tmpdir): picklefile = tmpdir.join("data.pickle") return Python(request.param, picklefile) -@pytest.factory(params=pythonlist) +@pytest.fixture(params=pythonlist) def python2(request, python1): return Python(request.param, python1.picklefile) diff --git a/doc/en/faq.txt b/doc/en/faq.txt index 34abe4de4..65946c9c2 100644 --- a/doc/en/faq.txt +++ b/doc/en/faq.txt @@ -125,7 +125,7 @@ the ``MYARG`` function argument. .. note:: - With pytest-2.3 you can use the :ref:`@pytest.factory` decorator + With pytest-2.3 you can use the :ref:`@pytest.fixture` decorator to mark a function as a funcarg factory. .. _`Convention over Configuration`: http://en.wikipedia.org/wiki/Convention_over_Configuration @@ -146,7 +146,7 @@ is not possible: policy - in real-world examples some combinations often should not run. -However, with pytest-2.3 you can use the :ref:`@pytest.factory` decorator +However, with pytest-2.3 you can use the :ref:`@pytest.fixture` decorator and specify ``params`` so that all tests depending on the factory-created resource will run multiple times with different parameters. diff --git a/doc/en/funcarg_compare.txt b/doc/en/funcarg_compare.txt index 084be31e4..b1700aaa0 100644 --- a/doc/en/funcarg_compare.txt +++ b/doc/en/funcarg_compare.txt @@ -63,9 +63,9 @@ new facilities. Direct scoping of funcarg factories -------------------------------------------------------- -Instead of calling cached_setup(), you can use the :ref:`@pytest.factory <@pytest.factory>` decorator and directly state the scope:: +Instead of calling cached_setup(), you can use the :ref:`@pytest.fixture <@pytest.fixture>` decorator and directly state the scope:: - @pytest.factory(scope="session") + @pytest.fixture(scope="session") def db(request): # factory will only be invoked once per session - db = DataBase() @@ -87,7 +87,7 @@ or implement a ``pytest_generate_tests`` hook to perform parametrization, i.e. calling a test multiple times with different value sets. pytest-2.3 introduces a decorator for use on the factory itself:: - @pytest.factory(params=["mysql", "pg"]) + @pytest.fixture(params=["mysql", "pg"]) def db(request): ... # use request.param @@ -104,7 +104,7 @@ functions/classes were parametrized via Of course it's perfectly fine to combine parametrization and scoping:: - @pytest.factory(scope="session", params=["mysql", "pg"]) + @pytest.fixture(scope="session", params=["mysql", "pg"]) def db(request): if request.param == "mysql": db = MySQL() @@ -125,7 +125,7 @@ When using the ``@factory`` decorator the name of the function denotes the name under which the resource can be accessed as a function argument:: - @pytest.factory() + @pytest.fixture() def db(request): ... diff --git a/doc/en/funcargs.txt b/doc/en/funcargs.txt index 9cb526c32..37ec71927 100644 --- a/doc/en/funcargs.txt +++ b/doc/en/funcargs.txt @@ -51,7 +51,7 @@ inputs lead to certain outputs. Concretely, there are three main means of funcarg management: -* a `@pytest.factory`_ marker to define resource factories, +* 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 @@ -77,9 +77,9 @@ 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.factory`: +.. _`@pytest.fixture`: -``@pytest.factory``: Creating parametrized, scoped resources +``@pytest.fixture``: Creating parametrized, scoped resources ===================================================================== Basic funcarg injection example @@ -91,7 +91,7 @@ and a funcarg:: # content of ./test_simplefactory.py import pytest - @pytest.factory() + @pytest.fixture() def myfuncarg(): return 42 @@ -99,7 +99,7 @@ and a funcarg:: assert myfuncarg == 17 Here, the ``test_function`` needs an object named ``myfuncarg`` and thus -py.test will discover and call the ``@pytest.factory`` marked ``myfuncarg`` +py.test will discover and call the ``@pytest.fixture`` marked ``myfuncarg`` factory function. Running the tests looks like this:: $ py.test test_simplefactory.py @@ -168,7 +168,7 @@ funcarg factories starts at test classes, then test modules, then Parametrizing test functions ========================================================================== -While the `@pytest.factory`_ decorator allows to define parametrization +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: @@ -365,18 +365,18 @@ the mechanism was extended and refined: * previously funcarg factories were specified with a special ``pytest_funcarg__NAME`` prefix instead of using the - ``@pytest.factory`` decorator. + ``@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.factory`` decorator allows to simply declare the scope + 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 funcarg factory code to use the `@pytest.factory`_ + and simplify your funcarg factory code to use the `@pytest.fixture`_ decorator instead. This will also allow to take advantage of the `automatic per-resource grouping`_ of tests. diff --git a/doc/en/unittest.txt b/doc/en/unittest.txt index 7c3f8a14b..bc2d84257 100644 --- a/doc/en/unittest.txt +++ b/doc/en/unittest.txt @@ -82,7 +82,7 @@ instances, based on a marker, you can do it using :ref:`pytest.mark`` and import pytest import unittest - @pytest.factory() + @pytest.fixture() def db(): class DummyDB: x = 1 diff --git a/testing/test_python.py b/testing/test_python.py index d710b5d61..a8d8f4153 100644 --- a/testing/test_python.py +++ b/testing/test_python.py @@ -744,7 +744,7 @@ class TestMarking: def test_accessmarker_function(self, testdir): testdir.makepyfile(""" import pytest - @pytest.factory() + @pytest.fixture() def markers(request): return request.node.markers @pytest.mark.XYZ @@ -758,7 +758,7 @@ class TestMarking: def test_accessmarker_dynamic(self, testdir): testdir.makeconftest(""" import pytest - @pytest.factory() + @pytest.fixture() def markers(request): return request.node.markers @@ -1674,11 +1674,11 @@ class TestFuncargFactory: def test_receives_funcargs(self, testdir): testdir.makepyfile(""" import pytest - @pytest.factory() + @pytest.fixture() def arg1(): return 1 - @pytest.factory() + @pytest.fixture() def arg2(arg1): return arg1 + 1 @@ -1694,11 +1694,11 @@ class TestFuncargFactory: def test_receives_funcargs_scope_mismatch(self, testdir): testdir.makepyfile(""" import pytest - @pytest.factory(scope="function") + @pytest.fixture(scope="function") def arg1(): return 1 - @pytest.factory(scope="module") + @pytest.fixture(scope="module") def arg2(arg1): return arg1 + 1 @@ -1717,12 +1717,12 @@ class TestFuncargFactory: testdir.makepyfile(""" import pytest l = [] - @pytest.factory(params=[1,2]) + @pytest.fixture(params=[1,2]) def arg1(request): l.append(1) return request.param - @pytest.factory() + @pytest.fixture() def arg2(arg1): return arg1 + 1 @@ -1739,11 +1739,11 @@ class TestFuncargFactory: testdir.makepyfile(""" import pytest - @pytest.factory() + @pytest.fixture() def fail(missing): return - @pytest.factory() + @pytest.fixture() def call_fail(fail): return @@ -1764,7 +1764,7 @@ class TestFuncargFactory: class arg1: def __init__(self, request): self.x = 1 - arg1 = pytest.factory()(arg1) + arg1 = pytest.fixture()(arg1) class MySetup: def __init__(self, request, arg1): @@ -1781,7 +1781,7 @@ class TestFuncargFactory: def test_request_can_be_overridden(self, testdir): testdir.makepyfile(""" import pytest - @pytest.factory() + @pytest.fixture() def request(request): request.a = 1 return request @@ -1863,7 +1863,7 @@ class TestSetupDiscovery: def perfunction(request, tmpdir): pass - @pytest.factory() + @pytest.fixture() def arg1(tmpdir): pass @pytest.setup() @@ -1918,7 +1918,7 @@ class TestSetupDiscovery: def enabled(parentnode, markers): return "needsdb" in markers - @pytest.factory(params=[1,2]) + @pytest.fixture(params=[1,2]) def db(request): return request.param @@ -1959,7 +1959,7 @@ class TestSetupManagement: testdir.makepyfile(""" import pytest l = [] - @pytest.factory(scope="module") + @pytest.fixture(scope="module") def arg(): l.append(1) return 0 @@ -1984,7 +1984,7 @@ class TestSetupManagement: testdir.makepyfile(""" import pytest l = [] - @pytest.factory(params=[1,2]) + @pytest.fixture(params=[1,2]) def arg(request): return request.param @@ -2010,7 +2010,7 @@ class TestSetupManagement: l = [] - @pytest.factory(scope="session", params=[1,2]) + @pytest.fixture(scope="session", params=[1,2]) def arg(request): return request.param @@ -2036,11 +2036,11 @@ class TestSetupManagement: l = [] - @pytest.factory(scope="function", params=[1,2]) + @pytest.fixture(scope="function", params=[1,2]) def farg(request): return request.param - @pytest.factory(scope="class", params=list("ab")) + @pytest.fixture(scope="class", params=list("ab")) def carg(request): return request.param @@ -2117,7 +2117,7 @@ class TestFuncargMarker: def test_parametrize(self, testdir): testdir.makepyfile(""" import pytest - @pytest.factory(params=["a", "b", "c"]) + @pytest.fixture(params=["a", "b", "c"]) def arg(request): return request.param l = [] @@ -2133,7 +2133,7 @@ class TestFuncargMarker: testdir.makepyfile(""" import pytest l = [] - @pytest.factory(scope="module") + @pytest.fixture(scope="module") def arg(): l.append(1) return 1 @@ -2155,7 +2155,7 @@ class TestFuncargMarker: testdir.makepyfile(""" import pytest l = [] - @pytest.factory(scope="module") + @pytest.fixture(scope="module") def arg(): l.append(1) return 1 @@ -2178,7 +2178,7 @@ class TestFuncargMarker: import pytest finalized = [] created = [] - @pytest.factory(scope="module") + @pytest.fixture(scope="module") def arg(request): created.append(1) assert request.scope == "module" @@ -2217,14 +2217,14 @@ class TestFuncargMarker: import pytest finalized = [] created = [] - @pytest.factory(scope="function") + @pytest.fixture(scope="function") def arg(request): pass """) testdir.makepyfile( test_mod1=""" import pytest - @pytest.factory(scope="session") + @pytest.fixture(scope="session") def arg(request): %s def test_1(arg): @@ -2239,14 +2239,14 @@ class TestFuncargMarker: def test_register_only_with_mark(self, testdir): testdir.makeconftest(""" import pytest - @pytest.factory() + @pytest.fixture() def arg(): return 1 """) testdir.makepyfile( test_mod1=""" import pytest - @pytest.factory() + @pytest.fixture() def arg(arg): return arg + 1 def test_1(arg): @@ -2258,7 +2258,7 @@ class TestFuncargMarker: def test_parametrize_and_scope(self, testdir): testdir.makepyfile(""" import pytest - @pytest.factory(scope="module", params=["a", "b", "c"]) + @pytest.fixture(scope="module", params=["a", "b", "c"]) def arg(request): return request.param l = [] @@ -2276,13 +2276,13 @@ class TestFuncargMarker: def test_scope_mismatch(self, testdir): testdir.makeconftest(""" import pytest - @pytest.factory(scope="function") + @pytest.fixture(scope="function") def arg(request): pass """) testdir.makepyfile(""" import pytest - @pytest.factory(scope="session") + @pytest.fixture(scope="session") def arg(arg): pass def test_mismatch(arg): @@ -2298,7 +2298,7 @@ class TestFuncargMarker: testdir.makepyfile(""" import pytest - @pytest.factory(scope="module", params=[1, 2]) + @pytest.fixture(scope="module", params=[1, 2]) def arg(request): return request.param @@ -2317,10 +2317,10 @@ class TestFuncargMarker: testdir.makeconftest(""" import pytest - @pytest.factory(scope="session", params="s1 s2".split()) + @pytest.fixture(scope="session", params="s1 s2".split()) def sarg(): pass - @pytest.factory(scope="module", params="m1 m2".split()) + @pytest.fixture(scope="module", params="m1 m2".split()) def marg(): pass """) @@ -2365,11 +2365,11 @@ class TestFuncargMarker: l = [] - @pytest.factory(scope="function", params=[1,2]) + @pytest.fixture(scope="function", params=[1,2]) def farg(request): return request.param - @pytest.factory(scope="class", params=list("ab")) + @pytest.fixture(scope="class", params=list("ab")) def carg(request): return request.param @@ -2411,14 +2411,14 @@ class TestFuncargMarker: testdir.makepyfile(""" import pytest - @pytest.factory(scope="function", params=[1, 2]) + @pytest.fixture(scope="function", params=[1, 2]) def arg(request): param = request.param request.addfinalizer(lambda: l.append("fin:%s" % param)) l.append("create:%s" % param) return request.param - @pytest.factory(scope="module", params=["mod1", "mod2"]) + @pytest.fixture(scope="module", params=["mod1", "mod2"]) def modarg(request): param = request.param request.addfinalizer(lambda: l.append("fin:%s" % param)) @@ -2455,7 +2455,7 @@ class TestFuncargMarker: testdir.makepyfile(""" import pytest - @pytest.factory(scope="module", params=[1, 2]) + @pytest.fixture(scope="module", params=[1, 2]) def arg(request): request.config.l = l # to access from outer x = request.param @@ -2484,7 +2484,7 @@ class TestFuncargMarker: testdir.makepyfile(""" import pytest - @pytest.factory(scope="function", params=[1, 2]) + @pytest.fixture(scope="function", params=[1, 2]) def arg(request): x = request.param request.addfinalizer(lambda: l.append("fin%s" % x)) @@ -2506,7 +2506,7 @@ class TestFuncargMarker: testdir.makepyfile(""" import pytest - @pytest.factory(scope="module", params=[1, 2]) + @pytest.fixture(scope="module", params=[1, 2]) def arg(request): return request.param @@ -2562,7 +2562,7 @@ class TestTestContextScopeAccess: def test_funcarg(self, testdir, scope, ok, error): testdir.makepyfile(""" import pytest - @pytest.factory(scope=%r) + @pytest.fixture(scope=%r) def arg(request): for x in %r: assert hasattr(request, x) @@ -2582,7 +2582,7 @@ class TestErrors: def test_subfactory_missing_funcarg(self, testdir): testdir.makepyfile(""" import pytest - @pytest.factory() + @pytest.fixture() def gen(qwe123): return 1 def test_something(gen): @@ -2618,7 +2618,7 @@ class TestTestContextVarious: def test_newstyle_with_request(self, testdir): testdir.makepyfile(""" import pytest - @pytest.factory() + @pytest.fixture() def arg(request): pass def test_1(arg): @@ -2630,7 +2630,7 @@ class TestTestContextVarious: def test_setupcontext_no_param(self, testdir): testdir.makepyfile(""" import pytest - @pytest.factory(params=[1,2]) + @pytest.fixture(params=[1,2]) def arg(request): return request.param @@ -2683,7 +2683,7 @@ def test_setup_funcarg_order(testdir): @pytest.setup() def fix1(): l.append(1) - @pytest.factory() + @pytest.fixture() def arg1(): l.append(2) def test_hello(arg1): @@ -2696,10 +2696,10 @@ def test_setup_funcarg_order(testdir): def test_request_funcargnames(testdir): testdir.makepyfile(""" import pytest - @pytest.factory() + @pytest.fixture() def arg1(): pass - @pytest.factory() + @pytest.fixture() def farg(arg1): pass @pytest.setup()