Skillversitas

Monkey Patching

The monkeypatch fixture is the lighter stub than the mock: the setattr replaces the attribute (the module global, the class method), the setenv/delenv the os.environ, the setitem/delitem the dict, and the auto-undo (the every change reverted on the teardown, the no patch context). The monkeypatch is the mock's sibling for the simple replace (the no call record, the no return_value - the value is the set, not the configured). This chapter covers the setattr (the module global, the class method, the raising), the setenv/delenv (the os.environ), the setitem/delitem (the dict), and the auto-undo (the teardown's revert, the monkeypatch.undo).

The setattr: The Attribute Replace

# the setattr: the the REPLACE (the value,
#   the)
import pytest
import texttools.config

def test_config_value(monkeypatch):
    # the module GLOBAL (the
    #   the):
    monkeypatch.setattr(texttools.config, "MAX_LEN", 10)
    assert texttools.config.MAX_LEN == 10
    # the
    #   the

def test_method(monkeypatch):
    # the CLASS METHOD (the
    #   the):
    monkeypatch.setattr("texttools.io.write_file",
                        lambda path, content: None)
    export_words("a b", "out.txt")
    # the

The setattr is the attribute replace: the monkeypatch.setattr(texttools.config, "MAX_LEN", 10) the module global (the object plus the name, the value), the monkeypatch.setattr("texttools.io.write_file", func) the string form (the target.attr the dotted, the raising the AttributeError when the attribute is absent), and the monkeypatch.setattr(obj, "method", func) the class method (the instance the no patch, the class the all instance). The monkeypatch is the fixture (the function scope, the no import of the mock, the no patch context) - the simple replace (the value the set, the no call record, the no return_value), and the auto-undo (the next section) is the teardown's revert. The rule is the one: the monkeypatch is the simple replace (the config, the flag, the small function), the mock is the behavior (the call record, the side_effect, the autospec) - the two are the stub's two weights, and the next section's setenv is the environment half.

The setenv and the setitem

# the setenv/delenv: the the os.environ (the
#   the)
import os
import pytest

def test_env(monkeypatch):
    monkeypatch.setenv("TEXTTOOLS_LANG", "en")
    assert os.environ["TEXTTOOLS_LANG"] == "en"
    # the
    #   the

def test_delenv(monkeypatch):
    monkeypatch.delenv("TEXTTOOLS_DEBUG", raising=False)
    assert "TEXTTOOLS_DEBUG" not in os.environ
    # the raising=False: the
    #   the

def test_setitem(monkeypatch):
    # the setitem/delitem: the the DICT (the
    #   the)
    d = {"a": 1}
    monkeypatch.setitem(d, "b", 2)
    assert d == {"a": 1, "b": 2}
    monkeypatch.delitem(d, "a")
    assert d == {"b": 2}

The setenv/delenv is the os.environ (the setenv("TEXTTOOLS_LANG", "en") the set, the delenv the remove, the raising=False the no-KeyError on the absent) - the environment the test controls (the CI flag, the LANG), and the setitem/delitem is the dict (the setitem(d, "b", 2) the set, the delitem(d, "a") the remove, the raising the KeyError the absent). The monkeypatch is the one fixture for the three targets (the attribute, the env, the dict), and the auto-undo is the teardown's revert - the next section. The rule is the one: the monkeypatch is the simple replace (the no call record, the value the set), the mock is the behavior (the return_value, the side_effect) - the two are the stub's two weights, and the monkeypatch the lighter (the no import, the no MagicMock), the mock the heavier (the). The next section's auto-undo is the monkeypatch's guarantee.

The Auto-Undo

# the AUTO-UNDO: the the every change the REVERT on
#   the
#   the
import pytest

def test_undo(monkeypatch):
    import texttools.config
    old = texttools.config.MAX_LEN
    monkeypatch.setattr(texttools.config, "MAX_LEN", 99)
    assert texttools.config.MAX_LEN == 99
    # the test ENDS: the REVERT
    #   the

def test_next_sees_old():
    import texttools.config
    # the
    #   the
    assert texttools.config.MAX_LEN != 99
    # the

The auto-undo is the monkeypatch's guarantee: the every change (the setattr, the setenv, the setitem) is reverted on the teardown (the monkeypatch fixture's finalizer, the monkeypatch.undo the manual), so the next test sees the original (the test_next_sees_old the MAX_LEN not the 99, the no cross-test leak). The monkeypatch is the function scope (the fresh per test, the undo on the per-test teardown), and the monkeypatch.context() (the context manager) is the no-fixture form (the with monkeypatch.context() as m: the manual scope, the m.undo() the explicit). The rule is the one: the monkeypatch is the change-plus-undo (the no manual restore, the no try/finally), the patch is the replace-plus-restore (the mock's), and the monkeypatch the lighter (the no mock object, the value the set) - the next chapter's async testing is the async def test the pytest-asyncio runs.

The Working Rules

  • The setattr is the attribute replace, the raising the AttributeError. The setattr(obj, "attr", value) the object-plus-name, the setattr("a.b.attr", value) the dotted, the raising=False the no-error on the absent - the module global, the class method, the instance the no.
  • The setenv/delenv the os.environ, the setitem/delitem the dict. The setenv the set, the delenv the remove (the raising=False the absent), the setitem the dict's set, the delitem the remove - the one fixture the three targets.
  • The auto-undo is the teardown's revert, the monkeypatch.undo the manual. The every change reverted (the setattr, the setenv, the setitem), the next test sees the original (the no cross-test leak), the monkeypatch.context() the no-fixture form.
  • The monkeypatch the simple replace, the mock the behavior. The monkeypatch the value the set (the no call record, the no return_value), the mock the side_effect plus the assert_called - the two weights, the monkeypatch the lighter.

Summary

The monkeypatch fixture is the lighter stub than the mock: the setattr replaces the attribute (the module global, the class method, the raising the AttributeError), the setenv/delenv the os.environ (the raising=False the absent), the setitem/delitem the dict, and the auto-undo reverts the every change on the teardown (the monkeypatch.undo the manual, the monkeypatch.context() the no-fixture). The monkeypatch is the function scope (the fresh per test, the undo on the per-test teardown, the no cross-test leak), and the rule is the split: the monkeypatch is the simple replace (the value the set, the no call record), the mock is the behavior (the return_value, the side_effect, the assert_called) - the two weights, the monkeypatch the lighter (the no import, the no MagicMock). The next chapter's async def test is the pytest-asyncio the plugin runs, the @pytest.mark.asyncio the mark, and the event loop the fixture the plugin manages.