Test doubles

unittest.mock.patch when a test should not touch the real world.

When a function talks to time, random, or a file you do not want to touch, patch it. unittest.mock replaces that name for the duration of the test.

Goal

Patch time.time and json.load so tests stay deterministic.

Patch a clock

import time
import unittest
from unittest.mock import patch

def stamp(city):
    return f"{city}@{int(time.time())}"

class StampTests(unittest.TestCase):
    @patch("time.time", return_value=1_700_000_000)
    def test_stamp(self, _mocked):
        self.assertEqual(stamp("Nairobi"), "Nairobi@1700000000")

suite = unittest.defaultTestLoader.loadTestsFromTestCase(StampTests)
result = unittest.TextTestRunner(verbosity=2).run(suite)
print("ok" if result.wasSuccessful() else "failed")

Patch json.load

import json
import unittest
from unittest.mock import patch

def load_city(path):
    with open(path, encoding="utf-8") as handle:
        return json.load(handle)["city"]

class ConfigTests(unittest.TestCase):
    @patch("json.load", return_value={"city": "Mombasa"})
    @patch("builtins.open")
    def test_load_city(self, _open, _load):
        self.assertEqual(load_city("config.json"), "Mombasa")

suite = unittest.defaultTestLoader.loadTestsFromTestCase(ConfigTests)
result = unittest.TextTestRunner(verbosity=2).run(suite)
print("ok" if result.wasSuccessful() else "failed")

json.load returns a dict, so no real file is read. open is still patched so the with block does not hit disk.

Tip

Patch where the name is used, not where it is defined. If kiosk.py does from json import load, patch kiosk.load.