Make monkeypatch invocation-scoped
This commit is contained in:
parent
4f2bf965cb
commit
05f3422d7c
|
@ -10,7 +10,7 @@ import pytest
|
||||||
RE_IMPORT_ERROR_NAME = re.compile("^No module named (.*)$")
|
RE_IMPORT_ERROR_NAME = re.compile("^No module named (.*)$")
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture(scope='invocation')
|
||||||
def monkeypatch(request):
|
def monkeypatch(request):
|
||||||
"""The returned ``monkeypatch`` fixture provides these
|
"""The returned ``monkeypatch`` fixture provides these
|
||||||
helper methods to modify objects, dictionaries or os.environ::
|
helper methods to modify objects, dictionaries or os.environ::
|
||||||
|
@ -25,9 +25,11 @@ def monkeypatch(request):
|
||||||
monkeypatch.chdir(path)
|
monkeypatch.chdir(path)
|
||||||
|
|
||||||
All modifications will be undone after the requesting
|
All modifications will be undone after the requesting
|
||||||
test function has finished. The ``raising``
|
test function or fixture has finished. The ``raising``
|
||||||
parameter determines if a KeyError or AttributeError
|
parameter determines if a KeyError or AttributeError
|
||||||
will be raised if the set/deletion operation has no target.
|
will be raised if the set/deletion operation has no target.
|
||||||
|
|
||||||
|
This fixture is ``invocation``-scoped.
|
||||||
"""
|
"""
|
||||||
mpatch = MonkeyPatch()
|
mpatch = MonkeyPatch()
|
||||||
request.addfinalizer(mpatch.undo)
|
request.addfinalizer(mpatch.undo)
|
||||||
|
@ -97,7 +99,8 @@ notset = Notset()
|
||||||
|
|
||||||
|
|
||||||
class MonkeyPatch:
|
class MonkeyPatch:
|
||||||
""" Object keeping a record of setattr/item/env/syspath changes. """
|
""" Object returned by the ``monkeypatch`` fixture keeping a record of setattr/item/env/syspath changes.
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self._setattr = []
|
self._setattr = []
|
||||||
|
|
|
@ -9,9 +9,6 @@ Invocation-scoped fixtures
|
||||||
This feature is experimental, so if decided that it brings too much problems
|
This feature is experimental, so if decided that it brings too much problems
|
||||||
or considered too complicated it might be removed in pytest ``3.1``.
|
or considered too complicated it might be removed in pytest ``3.1``.
|
||||||
|
|
||||||
``tmpdir`` and ``monkeypatch`` might become ``invocation`` scoped
|
|
||||||
fixtures in the future if decided to keep invocation-scoped fixtures.
|
|
||||||
|
|
||||||
Fixtures can be defined with ``invocation`` scope, meaning that the fixture
|
Fixtures can be defined with ``invocation`` scope, meaning that the fixture
|
||||||
can be requested by fixtures from any scope, but when they do they assume
|
can be requested by fixtures from any scope, but when they do they assume
|
||||||
the same scope as the fixture requesting it.
|
the same scope as the fixture requesting it.
|
||||||
|
|
|
@ -6,7 +6,7 @@ Monkeypatching/mocking modules and environments
|
||||||
|
|
||||||
Sometimes tests need to invoke functionality which depends
|
Sometimes tests need to invoke functionality which depends
|
||||||
on global settings or which invokes code which cannot be easily
|
on global settings or which invokes code which cannot be easily
|
||||||
tested such as network access. The ``monkeypatch`` function argument
|
tested such as network access. The ``monkeypatch`` fixture
|
||||||
helps you to safely set/delete an attribute, dictionary item or
|
helps you to safely set/delete an attribute, dictionary item or
|
||||||
environment variable or to modify ``sys.path`` for importing.
|
environment variable or to modify ``sys.path`` for importing.
|
||||||
See the `monkeypatch blog post`_ for some introduction material
|
See the `monkeypatch blog post`_ for some introduction material
|
||||||
|
@ -14,6 +14,9 @@ and a discussion of its motivation.
|
||||||
|
|
||||||
.. _`monkeypatch blog post`: http://tetamap.wordpress.com/2009/03/03/monkeypatching-in-unit-tests-done-right/
|
.. _`monkeypatch blog post`: http://tetamap.wordpress.com/2009/03/03/monkeypatching-in-unit-tests-done-right/
|
||||||
|
|
||||||
|
As of pytest-3.0, the ``monkeypatch`` fixture is :ref:`invocation-scoped <invocation_scoped_fixture>`
|
||||||
|
meaning it can be requested from fixtures of any scope.
|
||||||
|
|
||||||
Simple example: monkeypatching functions
|
Simple example: monkeypatching functions
|
||||||
---------------------------------------------------
|
---------------------------------------------------
|
||||||
|
|
||||||
|
@ -53,27 +56,31 @@ This autouse fixture will be executed for each test function and it
|
||||||
will delete the method ``request.session.Session.request``
|
will delete the method ``request.session.Session.request``
|
||||||
so that any attempts within tests to create http requests will fail.
|
so that any attempts within tests to create http requests will fail.
|
||||||
|
|
||||||
example: setting an attribute on some class
|
example: setting an environment variable for the test session
|
||||||
------------------------------------------------------
|
-------------------------------------------------------------
|
||||||
|
|
||||||
If you need to patch out ``os.getcwd()`` to return an artificial
|
If you would like for an environment variable to be
|
||||||
value::
|
configured for the entire test session, you can add this to your
|
||||||
|
top-level ``conftest.py`` file:
|
||||||
|
|
||||||
def test_some_interaction(monkeypatch):
|
.. code-block:: python
|
||||||
monkeypatch.setattr("os.getcwd", lambda: "/")
|
|
||||||
|
|
||||||
which is equivalent to the long form::
|
# content of conftest.py
|
||||||
|
@pytest.fixture(scope='session', autouse=True)
|
||||||
|
def enable_debugging(monkeypatch):
|
||||||
|
monkeypatch.setenv("DEBUGGING_VERBOSITY", "4")
|
||||||
|
|
||||||
def test_some_interaction(monkeypatch):
|
This auto-use fixture will set the ``DEBUGGING_VERBOSITY`` environment variable for
|
||||||
import os
|
the entire test session.
|
||||||
monkeypatch.setattr(os, "getcwd", lambda: "/")
|
|
||||||
|
Note that the ability to use a ``monkeypatch`` fixture from a ``session``-scoped
|
||||||
|
fixture was added in pytest-3.0.
|
||||||
|
|
||||||
|
|
||||||
Method reference of the monkeypatch function argument
|
Method reference of the monkeypatch fixture
|
||||||
-----------------------------------------------------
|
-------------------------------------------
|
||||||
|
|
||||||
.. autoclass:: monkeypatch
|
.. autoclass:: MonkeyPatch
|
||||||
:members: setattr, replace, delattr, setitem, delitem, setenv, delenv, syspath_prepend, chdir, undo
|
:members: setattr, replace, delattr, setitem, delitem, setenv, delenv, syspath_prepend, chdir, undo
|
||||||
|
|
||||||
``monkeypatch.setattr/delattr/delitem/delenv()`` all
|
``monkeypatch.setattr/delattr/delitem/delenv()`` all
|
||||||
|
|
|
@ -328,4 +328,36 @@ def test_issue1338_name_resolving():
|
||||||
try:
|
try:
|
||||||
monkeypatch.delattr('requests.sessions.Session.request')
|
monkeypatch.delattr('requests.sessions.Session.request')
|
||||||
finally:
|
finally:
|
||||||
monkeypatch.undo()
|
monkeypatch.undo()
|
||||||
|
|
||||||
|
|
||||||
|
def test_invocation_scoped_monkeypatch(testdir):
|
||||||
|
testdir.makeconftest("""
|
||||||
|
import pytest
|
||||||
|
import sys
|
||||||
|
|
||||||
|
@pytest.fixture(scope='module')
|
||||||
|
def stamp_sys(monkeypatch):
|
||||||
|
monkeypatch.setattr(sys, 'module_stamped', True, raising=False)
|
||||||
|
""")
|
||||||
|
testdir.makepyfile(test_inv_mokeypatch_1="""
|
||||||
|
import sys
|
||||||
|
|
||||||
|
def test_stamp_1(monkeypatch, stamp_sys):
|
||||||
|
assert sys.module_stamped
|
||||||
|
monkeypatch.setattr(sys, 'function_stamped', True, raising=False)
|
||||||
|
assert sys.function_stamped
|
||||||
|
|
||||||
|
def test_stamp_2(monkeypatch):
|
||||||
|
assert sys.module_stamped
|
||||||
|
assert not hasattr(sys, 'function_stamped')
|
||||||
|
""")
|
||||||
|
testdir.makepyfile(test_inv_mokeypatch_2="""
|
||||||
|
import sys
|
||||||
|
|
||||||
|
def test_no_stamps():
|
||||||
|
assert not hasattr(sys, 'module_stamped')
|
||||||
|
assert not hasattr(sys, 'function_stamped')
|
||||||
|
""")
|
||||||
|
result = testdir.runpytest()
|
||||||
|
result.stdout.fnmatch_lines(['*3 passed*'])
|
||||||
|
|
Loading…
Reference in New Issue