liveBook · Manning

Monkey patching

As explained in Refactoring to Rust

Monkey patching is the runtime replacement or modification of an object, function, or module implementation without changing its original source definition.

Monkey patching dynamically redefines a program item at runtime. In tests, it can replace a deep dependency or real I/O resource with a controlled alternative, such as swapping a Rust-backed function for its Python implementation. section 7.2.1

A focused comparison test follows these steps:

  • Keep the production function unchanged while retaining the original Python implementation as a test baseline. section 7.2.1
  • Compute one result through the normal Rust-backed path. section 7.2.1
  • Use pytest.MonkeyPatch.context to temporarily replace main.rust_json.sum with python_sum. section 7.2.1
  • Compare both results, then expand the test with randomized inputs to expose behavioral differences. section 7.2.1

Comparing Python and Rust implementations

A helper first evaluates main.sum(input). Inside a MonkeyPatch.context block, it sets main.rust_json.sum to python_sum, evaluates main.sum(input) again, and asserts that the two results match. Introducing an extra +10 in the Rust function makes the assertion fail with visibly different values; rebuilding and correcting the code restores passing tests. section 7.2.1

Listing 7.17 Test comparing the output of Rust and Python versions
from pytest import MonkeyPatch

def test_compare_py_rust():
  compare_py_and_rust(
    ['{ "name": "Stokes Baker", "value": 954832 }']
  )

def python_sum(line):
  import json

  value = json.loads(line)
  return value['value'] + len(value['name'])

def compare_py_and_rust(input):
  rust_result = main.sum(input)

  with MonkeyPatch.context() as m:
    m.setattr(main.rust_json, 'sum', python_sum)
    py_result = main.sum(input)

  assert rust_result == py_result

The complete comparison test shows the scoped replacement, baseline function, assertion, and known input in one listing. section 7.2.1

How Refactoring to Rust covers it

How deep it goes
Explains scoped replacement, result comparison, failure validation, and randomized inputs without dwelling on Python syntax.
Examples
Demonstrates pytest replacing a Rust JSON-summing function with Python code, then detects an introduced Rust arithmetic bug.
What it assumes
Assumes basic Python testing and imported functions, while introducing pytest.MonkeyPatch and scoped replacement incrementally.
What you can do after
Diagnose behavioral differences between implementations by substituting a baseline function and asserting matching outputs.
How it connects
Connects monkey patching with unit testing, dependency injection, Python, Rust, and cross-language integration testing.

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Refactoring to Rust. The section links point to the parts of the book it draws on.