In Why your mock doesn鈥檛 work I explained this rule of mocking:Mock where the object is used, not where it鈥檚 defined.That blog post explained why that rule was important: often a mock doesn鈥檛 work at all if you do it wrong. But in some cases, the mock will work even if you don鈥檛 follow this rule, and then it can break much later. Why?Let鈥檚 say you have code like this:# user.pydef get_user_settings() -> str: with open(Path("~/settings.json").expanduser()) as f: return json.load(f)def add_two_settings() -> int: settings = get_user_settings() return settings["opt1"] + settings["opt2"]You write a simple test:def test_add_two_settings(): # NOTE: need to create ~/settings.json for this to work: # {"opt1": 10, "opt2": 7} assert add_two_settings() == 17As the comment in the test points out, the test will only pass if you create the correct settings.json file in your home directory. This is bad: you don鈥檛 want to require finicky environments for your tests to pass.The thing we want to avoid is opening a real file, so it鈥檚 a natural impulse to mock out open():# test_user.pyfrom io import StringIOfrom unittest.mock import patch@patch("builtins.open")def test_add_two_settings(mock_open): mock_open.return_value = StringIO('{"opt1": 10, "opt2": 7}') assert add_two_settings() == 17Nice, the test works without needing to create a file in our home directory!Much later...One day your test suite fails with an error like:... File ".../site-packages/coverage/python.py", line 55, in get_python_source source_bytes = read_python_source(try_filename) File ".../site-packages/coverage/python.py", line 39, in read_python_source return source.replace(b"\r\n", b"\n").replace(b"\r", b"\n") ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^TypeError: replace() argument 1 must be str, not bytesWhat happened!? Coverage.py code runs during your tests, invoked by the Python interpreter. The mock in the test changed the builtin open, so any use of it anywhere during the test is affected. In some cases, coverage.py needs t...
First seen: 2025-11-16 23:56
Last seen: 2025-11-17 18:47