# 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.

`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.