# Monkeypatching/mocking modules and environments

Sometimes tests need to invoke functionality which depends on global settings or which invokes code which cannot be easily tested such as network access. The `monkeypatch` function argument helps you to safely set/delete an attribute, dictionary item or environment variable or to modify `sys.path` for importing. See the monkeypatch blog post for some introduction material and a discussion of its motivation.

## Simple example: monkeypatching functions

If you want to pretend that `os.expanduser` returns a certain directory, you can use the `monkeypatch.setattr()` method to patch this function before calling into a function which uses it:

    # content of test_module.py
    import os.path
    def getssh(): # pseudo application code
        return os.path.join(os.path.expanduser("~admin"), '.ssh')

    def test_mytest(monkeypatch):
        def mockreturn(path):
            return '/abc'
        monkeypatch.setattr(os.path, 'expanduser', mockreturn)
        x = getssh()
        assert x == '/abc/.ssh'

Here our test function monkeypatches `os.path.expanduser` and then calls into an function that calls it. After the test function finishes the `os.path.expanduser` modification will be undone.

## example: preventing “requests” from remote operations

If you want to prevent the “requests” library from performing http requests in all your tests, you can do:

    # content of conftest.py
    import pytest
    @pytest.fixture(autouse=True)
    def no_requests(monkeypatch):
        monkeypatch.delattr("requests.sessions.Session.request")

This autouse fixture will be executed for each test function and it will delete the method `request.session.Session.request` so that any attempts within tests to create http requests will fail.

## example: setting an attribute on some class

If you need to patch out `os.getcwd()` to return an artificial value:

    def test_some_interaction(monkeypatch):
        monkeypatch.setattr("os.getcwd", lambda: "/")

which is equivalent to the long form:

    def test_some_interaction(monkeypatch):
        import os
        monkeypatch.setattr(os, "getcwd", lambda: "/")

## Method reference of the monkeypatch function argument

class `monkeypatch`

  Object keeping a record of setattr/item/env/syspath changes.

  `setattr`(target, name, value=<notset>, raising=True)

    Set attribute value on target, memorizing the old value. By default raise AttributeError if the attribute did not exist.

    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. Example: `monkeypatch.setattr("os.getcwd", lambda x: "/")` would set the `getcwd` function of the `os` module.

    The `raising` value determines if the setattr should fail if the attribute is not already present (defaults to True which means it will raise).

  `delattr`(target, name=<notset>, raising=True)

    Delete attribute `name` from `target`, by default raise AttributeError it the attribute did not previously exist.

    If no `name` is specified and `target` is a string it will be interpreted as a dotted import path with the last part being the attribute name.

    If `raising` is set to False, no exception will be raised if the attribute is missing.

  `setitem`(dic, name, value)

    Set dictionary entry `name` to value.

  `delitem`(dic, name, raising=True)

    Delete `name` from dict. Raise KeyError if it doesn’t exist.

    If `raising` is set to False, no exception will be raised if the key is missing.

  `setenv`(name, value, prepend=None)

    Set environment variable `name` to `value`. If `prepend` is a character, read the current environment variable value and prepend the `value` adjoined with the `prepend` character.

  `delenv`(name, raising=True)

    Delete `name` from the environment. Raise KeyError it does not exist.

    If `raising` is set to False, no exception will be raised if the environment variable is missing.

  `syspath_prepend`(path)

    Prepend `path` to `sys.path` list of import locations.

  `chdir`(path)

    Change the current working directory to the specified path. Path can be a string or a py.path.local object.

  `undo`()

    Undo previous changes. This call consumes the undo stack. Calling it a second time has no effect unless you do more monkeypatching after the undo call.

    There is generally no need to call undo(), since it is called automatically during tear-down.

    Note that the same monkeypatch fixture is used across a single test function invocation. If monkeypatch is used both by the test function itself and one of the test fixtures, calling undo() will undo all of the changes made in both functions.

`monkeypatch.setattr/delattr/delitem/delenv()` all by default raise an Exception if the target does not exist. Pass `raising=False` if you want to skip this check.