What is mock/monkeypatch? Link to heading

mock and monkeypatch is heavily used on unittest where people avoid actual network calls, i/o calls, etc.

This is so useful that people use unittest should know and use it freely without any difficulties.

Issues Link to heading

I would like to use mock, but when project is becoming really large, suddenly mock doesn’t work! (amazing isn’t it? but it’s not mock, it’s purely me.)

Being specific, in large project, when I use monkeypatch to setattr, I found it is not getting patched! How come???

Example Link to heading

# foo.py

def foo():
    print("I am in foo.py")
# bar.py
from foo import foo

def bar():
    print("inside bar")
    foo()

When you executing bar method, you will see:

“inside bar” “I am in foo.py”

Now, let’s patch it.

# test_bar.py
import pytest
from bar import bar

def test_bar(monkeypatch):
    # monkeypatch will be auto 'inject' in signature
    def patch():
        print("I am in test_bar.py")
    monkeypatch.setattr("foo.foo", patch)
    bar()

What you are expecting? If I am correct, you are expecting:

“inside bar” “I am in test_bar.py”

correct???

Well, surprise, you will see:

“inside bar” “I am in foo.py”

Again!

Why is that and how to solve it Link to heading

patch() works by (temporarily) changing the object that a name points to with another one. There can be many names pointing to any individual object, so for patching to work you must ensure that you patch the name used by the system under test.

This is from where to patch in unittest mock. Underlying pytest monkeypatch uses unittest mock somehow if I am remembering.

What does this mean?

It means in test_bar method, it’s not foo module who actually calls foo(), it’s bar module who actually calls foo(). Hence, it’s making sense to do:

# test_bar.py
import pytest
from bar import bar

def test_bar(monkeypatch):
    # monkeypatch will be auto 'inject' in signature
    def patch():
        print("I am in test_bar.py")
    monkeypatch.setattr("bar.foo", patch)
    bar()

Now if you execute, you will see:

“inside bar” “I am in test_bar.py”

It’s got patched!

If until now, you still haven’t got a clue, please see this, people are having the same thinking as you.

Understanding on why Link to heading

Let’s revisit this:

… so for patching to work you must ensure that you patch the name used by the system under test.

I am from Java/Csharp world, in that world, it’s doing patching to the original stuff (if I am still remembering). It grab the Interface and created a Mock object, and test “knows” to use that Mock object when it met the same object. If thinking more, it sounds like it uses flavor of DI, I might be wrong tho.

But in python, you have to figure out who is actually calling that “object/method”, and you MUST patch that specific object in order to work.

Sounds less object-ish of python, isn’t it?

A bit word Link to heading

With more and more developing using python, I realized there is less favor of object, even everything sounds like object in the code! Maybe you will find they are all 3 layers of application, but thinking deeply are those objects singletons? transmit? Now python becomes fun isn’t it…?