""" safely patch object attributes, dicts and environment variables. Usage ---------------- Use the `monkeypatch funcarg`_ to safely patch environment variables, object attributes or dictionaries. For example, if you want to set the environment variable ``ENV1`` and patch the ``os.path.abspath`` function to return a particular value during a test function execution you can write it down like this: .. sourcecode:: python def test_mytest(monkeypatch): monkeypatch.setenv('ENV1', 'myval') monkeypatch.setattr(os.path, 'abspath', lambda x: '/') ... # your test code The function argument will do the modifications and memorize the old state. After the test function finished execution all modifications will be reverted. See the `monkeypatch blog post`_ for an extensive discussion. To add to a possibly existing environment parameter you can use this example: .. sourcecode:: python def test_mypath_finding(monkeypatch): monkeypatch.setenv('PATH', 'x/y', prepend=":") # x/y will be at the beginning of $PATH .. _`monkeypatch blog post`: http://tetamap.wordpress.com/2009/03/03/monkeypatching-in-unit-tests-done-right/ """ import os def pytest_funcarg__monkeypatch(request): """The returned ``monkeypatch`` funcarg provides three helper methods to modify objects, dictionaries or os.environ:: monkeypatch.setattr(obj, name, value) monkeypatch.setitem(mapping, name, value) monkeypatch.setenv(name, value) All such modifications will be undone when the requesting test function finished its execution. """ monkeypatch = MonkeyPatch() request.addfinalizer(monkeypatch.finalize) return monkeypatch notset = object() class MonkeyPatch: def __init__(self): self._setattr = [] self._setitem = [] def setattr(self, obj, name, value): self._setattr.insert(0, (obj, name, getattr(obj, name, notset))) setattr(obj, name, value) def setitem(self, dictionary, name, value): self._setitem.insert(0, (dictionary, name, dictionary.get(name, notset))) dictionary[name] = value def setenv(self, name, value, prepend=None): value = str(value) if prepend and name in os.environ: value = value + prepend + os.environ[name] self.setitem(os.environ, name, value) def finalize(self): for obj, name, value in self._setattr: if value is not notset: setattr(obj, name, value) else: delattr(obj, name) for dictionary, name, value in self._setitem: if value is notset: del dictionary[name] else: dictionary[name] = value def test_setattr(): class A: x = 1 monkeypatch = MonkeyPatch() monkeypatch.setattr(A, 'x', 2) assert A.x == 2 monkeypatch.setattr(A, 'x', 3) assert A.x == 3 monkeypatch.finalize() assert A.x == 1 monkeypatch.setattr(A, 'y', 3) assert A.y == 3 monkeypatch.finalize() assert not hasattr(A, 'y') def test_setitem(): d = {'x': 1} monkeypatch = MonkeyPatch() monkeypatch.setitem(d, 'x', 2) monkeypatch.setitem(d, 'y', 1700) assert d['x'] == 2 assert d['y'] == 1700 monkeypatch.setitem(d, 'x', 3) assert d['x'] == 3 monkeypatch.finalize() assert d['x'] == 1 assert 'y' not in d def test_setenv(): monkeypatch = MonkeyPatch() monkeypatch.setenv('XYZ123', 2) import os assert os.environ['XYZ123'] == "2" monkeypatch.finalize() assert 'XYZ123' not in os.environ def test_setenv_prepend(): import os monkeypatch = MonkeyPatch() monkeypatch.setenv('XYZ123', 2, prepend="-") assert os.environ['XYZ123'] == "2" monkeypatch.setenv('XYZ123', 3, prepend="-") assert os.environ['XYZ123'] == "3-2" monkeypatch.finalize() assert 'XYZ123' not in os.environ def test_monkeypatch_plugin(testdir): reprec = testdir.inline_runsource(""" pytest_plugins = 'pytest_monkeypatch', def test_method(monkeypatch): assert monkeypatch.__class__.__name__ == "MonkeyPatch" """) res = reprec.countoutcomes() assert tuple(res) == (1, 0, 0), res