From d1d8c0389d9ca896a2be23fe5a163030c97be21c Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Mon, 15 Aug 2022 10:27:42 -0300 Subject: [PATCH] Add reference to the Where to patch docs in monkeypatch.setattr This should help users with the common issue of patching the wrong place. Also took the opportunity of using proper links in the monkeypatch introduction. Related to #10216 --- doc/en/how-to/monkeypatch.rst | 24 +++++++++++------------ src/_pytest/monkeypatch.py | 36 +++++++++++++++++++++++++++++------ 2 files changed, 41 insertions(+), 19 deletions(-) diff --git a/doc/en/how-to/monkeypatch.rst b/doc/en/how-to/monkeypatch.rst index 9214de77e..81edd00bd 100644 --- a/doc/en/how-to/monkeypatch.rst +++ b/doc/en/how-to/monkeypatch.rst @@ -14,18 +14,16 @@ environment variable, or to modify ``sys.path`` for importing. The ``monkeypatch`` fixture provides these helper methods for safely patching and mocking functionality in tests: -.. code-block:: python +* :meth:`monkeypatch.setattr(obj, name, value, raising=True) ` +* :meth:`monkeypatch.delattr(obj, name, raising=True) ` +* :meth:`monkeypatch.setitem(mapping, name, value) ` +* :meth:`monkeypatch.delitem(obj, name, raising=True) ` +* :meth:`monkeypatch.setenv(name, value, prepend=None) ` +* :meth:`monkeypatch.delenv(name, raising=True) ` +* :meth:`monkeypatch.syspath_prepend(path) ` +* :meth:`monkeypatch.chdir(path) ` +* :meth:`monkeypatch.context() ` - monkeypatch.setattr(obj, name, value, raising=True) - monkeypatch.setattr("somemodule.obj.name", value, raising=True) - monkeypatch.delattr(obj, name, raising=True) - monkeypatch.setitem(mapping, name, value) - monkeypatch.delitem(obj, name, raising=True) - monkeypatch.setenv(name, value, prepend=None) - monkeypatch.delenv(name, raising=True) - monkeypatch.syspath_prepend(path) - monkeypatch.chdir(path) - monkeypatch.context() All modifications will be undone after the requesting test function or fixture has finished. The ``raising`` @@ -64,8 +62,8 @@ and a discussion of its motivation. .. _`monkeypatch blog post`: https://tetamap.wordpress.com//2009/03/03/monkeypatching-in-unit-tests-done-right/ -Simple example: monkeypatching functions ----------------------------------------- +Monkeypatching functions +------------------------ Consider a scenario where you are working with user directories. In the context of testing, you do not want your test to depend on the running user. ``monkeypatch`` diff --git a/src/_pytest/monkeypatch.py b/src/_pytest/monkeypatch.py index abf676907..9de25e645 100644 --- a/src/_pytest/monkeypatch.py +++ b/src/_pytest/monkeypatch.py @@ -40,6 +40,7 @@ def monkeypatch() -> Generator["MonkeyPatch", None, None]: * :meth:`monkeypatch.delenv(name, raising=True) ` * :meth:`monkeypatch.syspath_prepend(path) ` * :meth:`monkeypatch.chdir(path) ` + * :meth:`monkeypatch.context() ` All modifications will be undone after the requesting test function or fixture has finished. The ``raising`` parameter determines if a :class:`KeyError` @@ -186,16 +187,39 @@ class MonkeyPatch: value: object = notset, raising: bool = True, ) -> None: - """Set attribute value on target, memorizing the old value. + """ + Set attribute value on target, memorizing the old value. - For convenience you can specify a string as ``target`` which + For example: + + .. code-block:: python + + import os + + monkeypatch.setattr(os, "getcwd", lambda: "/") + + The code above replaces the :func:`os.getcwd` function by a ``lambda`` which + always returns ``"/"``. + + For convenience, you can specify a string as ``target`` which will be interpreted as a dotted import path, with the last part - being the attribute name. For example, - ``monkeypatch.setattr("os.getcwd", lambda: "/")`` - would set the ``getcwd`` function of the ``os`` module. + being the attribute name: - Raises AttributeError if the attribute does not exist, unless + .. code-block:: python + + monkeypatch.setattr("os.getcwd", lambda: "/") + + Raises :class:`AttributeError` if the attribute does not exist, unless ``raising`` is set to False. + + **Where to patch** + + ``monkeypatch.setattr`` works by (temporarily) changing the object that a name points to with another one. + There can be many names pointing to any individual object, so for patching to work you must ensure + that you patch the name used by the system under test. + + See the section *Where to patch* in the :mod:`unittest.mock` docs for a complete explanation, which + is meant for :func:`unittest.mock.patch` but applies to ``monkeypatch.setattr`` as well. """ __tracebackhide__ = True import inspect