Description
Following the conventional wisdom that we should patch objects where they are looked up and not where they are defined,
I got confused by the code doing obs_mock = mocker.patch("bicycle.observer.Observer.changed")
because it seemed to me bicycle.observer.Observer.changed
is closer to where Observer was defined.
I thought the patching code should be obs_mock = mocker.patch("bicycle.gear.Observer.changed")
instead.
I tested the change with pytest -sv tests/test_bicycle.py::test_notifies_observers_when_cogs_change
.
To my surprise, it also passed.
So I added 2 lines to check for equivalence of the Observer class in 2 different files.
In gear.py
i added print(f"In gear.py: {id(Observer) =}")
In test_bicycle.py
i added print(f"In test_bicycle.py: {id(bicycle.gear.Observer) =}")
I got the same id when running the test.
This tells me it doesn't matter whether i patch bicycle.observer.Observer.changed
or bicycle.gear.Observer.changed
because they are the same object.
Then i thought why are they the same, and realized inside gear.py
it does from bicycle.observer import Observer
.
When pytest runs, it runs the entire test_bicycle.py
which triggers from bicycle.gear import Gear
.
This in turn causes the entire gear.py
to run (an unintuitive part of python import system) even though only Gear class is imported.
When gear.py runs, it imports Observer.
Then to push my understanding, I deleted from bicycle.observer import Observer
from test_bicycle.py
and was glad to see both mocker.patch("bicycle.observer.Observer.changed")
and mocker.patch("bicycle.gear.Observer.changed")
passed the test.
Previous statement assumes i only run 1 test with pytest -sv tests/test_bicycle.py::test_notifies_observers_when_cogs_change
, because other tests depending on Observer() will fail since it's not imported into test_bicycle.py
after my edit.
I guess this works because the Observer in gear.py came from the Observer in observer.py through from bicycle.observer import Observer
in gear.py
.
Please correct my understanding if any of the above is wrong.
Also hoping to hear commentary of when to do what/not to do what, relating to my edits mentioned above
This example was confusing also because usually, the test function imports the object it uses (https://docs.python.org/3/library/unittest.mock.html#where-to-patch).
In this example,
obs_mock = mocker.patch("bicycle.observer.Observer.changed")
wheel = Wheel(rim=26, tire=1.5)
gear = Gear(
chainring=52, cog=11, wheel=wheel, observer=obs_mock
)
the observer value used to initialize Gear came out of thin air instead of being imported.