Pytest path of mock.patch() for third party package
Question:
Why the 1st case succeed. But the 2nd case failed AssertionError: Expected 'Jenkins' to have been called
.
util.py
from jenkinsapi.jenkins import Jenkins
import os
class Util:
@staticmethod
def rm(filename):
os.remove(filename)
@staticmethod
def get_jenkins_instance():
Jenkins(
'host',
username='username',
password='password',
ssl_verify=False,
lazy=True)
test_util.py
import pytest
from util import Util
def test_util_remove(mocker):
m = mocker.patch('os.remove')
Util.rm('file')
m.assert_called()
def test_util_get_instance(mocker):
m = mocker.patch('jenkinsapi.jenkins.Jenkins')
Util.get_jenkins_instance()
m.assert_called()
Two files are in the same root folder.
Answers:
Not very clear what’s the differences between Python’s import
and from ... import ...
.
But if you use from ... import ...
, the mock looks as following:
util.py
from jenkinsapi.jenkins import Jenkins # <-- difference A
class Util:
@staticmethod
def get_jenkins_instance():
Jenkins(
'host',
username='username',
password='password',
ssl_verify=False,
lazy=True)
test_util.py
import pytest
from util import Util
def test_util_get_instance(mocker):
m = mocker.patch('util.Jenkins') # <-- difference B
Util.get_jenkins_instance()
m.assert_called()
If you use import
directly, the mock looks as following:
util.py
import jenkinsapi.jenkins # <-- difference A
class Util:
@staticmethod
def get_jenkins_instance():
jenkinsapi.jenkins.Jenkins(
'host',
username='username',
password='password',
ssl_verify=False,
lazy=True)
test_util.py
import pytest
from util import Util
def test_util_get_instance(mocker):
m = mocker.patch('jenkinsapi.jenkins.Jenkins') # <-- difference B
Util.get_jenkins_instance()
m.assert_called()
========== Edit (Aug 5, 2022) ==========
Here’s the reason why patched like this.
a.py
-> Defines SomeClass
b.py
-> from a import SomeClass
-> some_function instantiates SomeClass
Now we want to test some_function but we want to mock out SomeClass using patch(). The problem is that when we import module b, which we will have to do then it imports SomeClass from module a. If we use patch() to mock out a.SomeClass then it will have no effect on our test; module b already has a reference to the real SomeClass and it looks like our patching had no effect.
The key is to patch out SomeClass where it is used (or where it is looked up ). In this case some_function will actually look up SomeClass in module b, where we have imported it.
Why the 1st case succeed. But the 2nd case failed AssertionError: Expected 'Jenkins' to have been called
.
util.py
from jenkinsapi.jenkins import Jenkins
import os
class Util:
@staticmethod
def rm(filename):
os.remove(filename)
@staticmethod
def get_jenkins_instance():
Jenkins(
'host',
username='username',
password='password',
ssl_verify=False,
lazy=True)
test_util.py
import pytest
from util import Util
def test_util_remove(mocker):
m = mocker.patch('os.remove')
Util.rm('file')
m.assert_called()
def test_util_get_instance(mocker):
m = mocker.patch('jenkinsapi.jenkins.Jenkins')
Util.get_jenkins_instance()
m.assert_called()
Two files are in the same root folder.
Not very clear what’s the differences between Python’s import
and from ... import ...
.
But if you use from ... import ...
, the mock looks as following:
util.py
from jenkinsapi.jenkins import Jenkins # <-- difference A
class Util:
@staticmethod
def get_jenkins_instance():
Jenkins(
'host',
username='username',
password='password',
ssl_verify=False,
lazy=True)
test_util.py
import pytest
from util import Util
def test_util_get_instance(mocker):
m = mocker.patch('util.Jenkins') # <-- difference B
Util.get_jenkins_instance()
m.assert_called()
If you use import
directly, the mock looks as following:
util.py
import jenkinsapi.jenkins # <-- difference A
class Util:
@staticmethod
def get_jenkins_instance():
jenkinsapi.jenkins.Jenkins(
'host',
username='username',
password='password',
ssl_verify=False,
lazy=True)
test_util.py
import pytest
from util import Util
def test_util_get_instance(mocker):
m = mocker.patch('jenkinsapi.jenkins.Jenkins') # <-- difference B
Util.get_jenkins_instance()
m.assert_called()
========== Edit (Aug 5, 2022) ==========
Here’s the reason why patched like this.
a.py
-> Defines SomeClass
b.py
-> from a import SomeClass
-> some_function instantiates SomeClass
Now we want to test some_function but we want to mock out SomeClass using patch(). The problem is that when we import module b, which we will have to do then it imports SomeClass from module a. If we use patch() to mock out a.SomeClass then it will have no effect on our test; module b already has a reference to the real SomeClass and it looks like our patching had no effect.
The key is to patch out SomeClass where it is used (or where it is looked up ). In this case some_function will actually look up SomeClass in module b, where we have imported it.