I am trying to mock the get function from requests.session and somehow it does not end up happening.
I have the following code:
#main.py
import requests
def function_with_get():
c = requests.session()
c.get('https://awesome_url.com')
# do some other stuff
return c
def second_function_with_get(client):
c.get('https://awesome_url.com')
# do some other stuff
#test.py
from unittest import mock
from django.test import TestCase
class Testing(TestCase):
#mock.patch('main.requests.session.get)
#mock.patch('main.requests.session)
def test_which_fails_because_of_get(mock_sess, mock_get):
client = function_with_get()
second_function_with_get(client)
assertEqual(mock_requests_session_get.call_count, 2)
The test throws an assertion error that mock_get is called 0 times (0 != 2)
How should the get function of requests.session() be mocked?
It seems that you are already mocking the requests session - since it is a MagicMock, you don't need to additionally mock the get method itself - checking for calls on the session will be enough.
So, your test.py could look like this:
#test.py
from unittest import mock
from unittest import TestCase
from main import function_with_get, second_function_with_get
class Testing(TestCase):
#mock.patch('main.requests.session')
def test_which_fails_because_of_get(self, mock_sess):
client = function_with_get()
second_function_with_get(client)
self.assertEqual(mock_sess.return_value.get.call_count, 2)
You could also try to create your own SessionMock class with the get method mocked, but it would require also proper setting (or resetting) it for each test. Personally, I usually find using MagicMock and its return_value chain easier.
Related
I am struggling to wrap my head around the unittest.mock docs and the many examples on SO.
I have a class with a method:
my_class.py
import requests
class MyClass:
def do_stuff(do_it=True):
if do_it:
requests.get('https://someapi.com')
And I want a unit test that checks that do_stuff tries to call the api using unittest.mock
I have a test (very simplified):
test_my_class.py
from django.test import TestCase
class TestMyClass(TestCase)
def test_do_stuff_uses_api(self):
MyClass().do_stuff()
# How to assert that requests.get() method was called once?
Checkout the package requests_mock
https://requests-mock.readthedocs.io/en/latest/history.html#called
from django.test import TestCase
import requests_mock
class TestMyClass(TestCase)
def test_do_stuff_uses_api(self):
with requests_mock.mock() as m:
m.get('https://someapi.com', json={....})
MyClass().do_stuff()
history = m.request_history
self.assertEqual(len(history), 1, "Should have been called once")
Does pytest provides functionality like unittest.mock to check if the mock was actually called once(or once with some parameter)?
Sample Source code:
my_package/my_module.py
from com.abc.validation import Validation
class MyModule:
def __init__(self):
pass
def will_call_other_package(self):
val = Validation()
val.do()
def run(self):
self.will_call_other_package()
Sample test code for the above source code:
test_my_module.py
import pytest
from pytest_mock import mocker
from my_package.my_module import MyModule
#pytest.fixture
def mock_will_call_other_package(mocker):
mocker.patch('my_package.my_module.will_call_other_package')
#pytest.mark.usefixtures("mock_will_call_other_package")
class TestMyModule:
def test_run(self):
MyModule().run()
#check `will_call_other_package` method is called.
#Looking for something similar to what unittest.mock provide
#mock_will_call_other_package.called_once
If you want to use a fixture that does the patching, you can move the patching into a fixture:
import pytest
from unittest import mock
from my_package.my_module import MyModule
#pytest.fixture
def mock_will_call_other_package():
with mock.patch('my_package.my_module.will_call_other_package') as mocked:
yield mocked
# the mocking will be reverted here, e.g. after the test
class TestMyModule:
def test_run(self, mock_will_call_other_package):
MyModule().run()
mock_will_call_other_package.assert_called_once()
Note that you have to use the fixture parameter in the test. Just using #pytest.mark.usefixtures will not give you access to the mock itself. You can still use it to be effective in all tests in the class, if you don't need to access the mock in all tests (or use autouse=True in the fixture).
Also note that you don't need pytest-mock here - but as mentioned by #hoefling, using it makes the fixture better readable, because you don't need the with clause :
#pytest.fixture
def mock_will_call_other_package(mocker):
yield mocker.patch('my_package.my_module.will_call_other_package')
As an aside: you don't need to import mocker. Fixtures are looked up by name, and available automatically if the respective plugin is installed.
You could try this:
import pytest
from my_package.my_module import MyModule
def test_run(mocker):
mocker.patch('my_package.my_module.will_call_other_package')
MyModule().run()
mock_will_call_other_package.assert_called_once()
First of all, you may not need the burden of an external library such as pytest_mock, because pytest already got you covered using the integration with unittest.
You also do not need to use the usefixtures because whenever you need a fixture, you just receive it in your test method.
An ideal scenario based on your own code would look similar to this:
import pytest
from unittest.mock import patch
from com.abc.validation import Validation
class MyModule:
def __init__(self):
pass
def will_call_other_package(self):
val = Validation()
val.do()
def run(self):
self.will_call_other_package()
#pytest.fixture
def call_other_module():
with patch("my_package.my_module.MyModule.will_call_other_package") as _patched:
yield _patched
class TestMyModule:
def test_run_will_call_other_package(self, call_other_module):
call_other_module.assert_not_called()
obj = MyModule()
call_other_module.assert_not_called()
obj.run()
call_other_module.assert_called_once()
And also if you want to make sure that you did infact patch the target MyModule.will_call_other_package, modify your test like this:
class TestMyModule:
def test_run_will_call_other_package(self, call_other_module):
call_other_module.assert_not_called()
obj = MyModule()
call_other_module.assert_not_called()
obj.run()
call_other_module.assert_called_once()
assert False, (MyModule.will_call_other_package, call_other_module)
And you'll see something similar to this:
AssertionError: (<MagicMock name='will_call_other_package' id='140695551841328'>, <MagicMock name='will_call_other_package' id='140695551841328'>)
As you can see the id of both objects are the same, confirming our experiment was successful.
I'm writing unit tests for a piece of code that uses zeep to access a SOAP API so I want to mock out zeep. In my actual code, it looks something like this:
from zeep import Client
def do_something():
client = Client("...")
In my test, I'm doing this:
from unittest import mock
#mock.patch('zeep.Client')
def test_do_somethi(self, MockedClient):
do_something()
The Client that the actual function is obtaining, is the actual zeep client and not my mock. I also tried:
#mock.patch('zeep.client.Client')
and the result was the same.
I also tried:
def test_do_something(self):
with mock.patch('zeep.client.Client') as MockedClient:
do_something()
with no difference.
Any ideas why this isn't working?
When mock doesn't work, the first thing to look for is if you are patching the right name. Three common import scenarios:
(a) If you want to mock out zeep but you import like
from zeep import Client
and your tests are in the same file, you patch Client not zeep.Client.
(b) If instead you import it like
import zeep
and then use zeep.Client in SUT code, then you patch zeep.Client.
(c) If you are testing code that lies in some other module (like mymodule) and you import zeep there with
from zeep import Client # (1)
then in your test module you
import mymodule
then you patch mymodule.Client, ... or mymodule.zeep.Client if you used the alternative import zeep form in (1).
You must patch the method/class from file you are using it. If you want to patch Client and it is imported in some_file.py you must import it from it, and not from lib (zeep.Client)
Here is an example using the official doc from Zeep.
some_lib.py
from zeep import Client
def do_something():
wsdl = 'http://www.soapclient.com/xml/soapresponder.wsdl'
client = zeep.Client(wsdl=wsdl)
return client.service.Method1('Zeep', 'is cool')
test_connection.py
from some_lib import do_something
from unittest.mock import patch
#patch('some_lib.Client')
def test_do_something(mock_zeep):
res = do_something()
assert mock_zeep.call_count == 1
if __name__ == '__main__':
test_soap_conn()
I have the following code that I'm attempting to create a test (still work in progress):
from core.tests import BaseTestCase
from core.views import get_request
from entidades.forms import InstituicaoForm
from mock import patch
class InstituicaoFormTestCase(BaseTestCase):
def setUp(self):
super(InstituicaoFormTestCase, self).setUp()
#patch('get_request', return_value={'user': 'usuario_qualquer'})
def test_salva_instituicao_quando_informaram_convenio():
import pdb
pdb.set_trace()
form = InstituicaoForm()
it fails because when I try to create a InstituicaoForm, a get_request is called:
def get_request():
return getattr(THREAD_LOCAL, 'request', None)
and it trows this error
entidades/tests.py:11: in <module>
class InstituicaoFormTestCase(BaseTestCase):
entidades/tests.py:16: in InstituicaoFormTestCase
#patch('get_request', return_value={'user': 'usuario_qualquer'})
.tox/unit/local/lib/python2.7/site-packages/mock/mock.py:1670: in patch
getter, attribute = _get_target(target)
.tox/unit/local/lib/python2.7/site-packages/mock/mock.py:1522: in _get_target
(target,))
E TypeError: Need a valid target to patch. You supplied: 'get_request'
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> entering PDB >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
> /home/vinicius/telessaude/.tox/unit/local/lib/python2.7/site-packages/mock/mock.py(1522)_get_target()
-> (target,))
What am I doing wrong? How should mock this get_request() method?
I think the specific thing you're trying to do can be done like this:
#patch('core.views.get_request', return_value={'user': 'usuario_qualquer'})
But you should also look at the Django testing documentation, if you haven't already. You can use the testing client to fake the web request.
If you want to experiment with mock tests that don't access a database, check out Django Mock Queries. (I'm a small contributor to that project.) I've also tried mocking views, but it's fiddly.
I am comfortable using dependancy injection with Java's Mockito library, but have little experience using Python3's unittest.mock module. I am trying to assert that the Request instance's prepare method gets called. However the test fails on the assertion self.assertTrue(mock_request.prepare.called). Can someone please advise me on how to get this test passing?
import requests
import unittest
from unittest import mock
class Engine(object):
def get(self, **kwargs):
session = requests.Session()
req = requests.Request('GET', 'http://www.google.com', params=kwargs).prepare()
response = session.send(req, timeout=1)
class TestEngine(unittest.TestCase):
#mock.patch('requests.Session')
#mock.patch('requests.Request')
def test_get(self, mock_request, mock_session):
e = Engine()
e.get()
self.assertTrue(mock_request.called)
self.assertTrue(mock_request.prepare.called)
if __name__ == '__main__':
unittest.main()
Your code never accesses prepare on Request directly. The method is accessed on the return value of a call to Request(), so test for that instead by using the Mock.return_value attribute:
self.assertTrue(mock_request.return_value.prepare.called)
When debugging mock issues, I find it helpful to print out the Mock.mock_calls attribute for the top-level mock object; for your test printing mock_request.mock_calls produces:
[call('GET', 'http://www.google.com', params={}), call().prepare()]
showing that call().prepare() was indeed accessed (and the result of a call() is usually accessible via the Mock.return_value attribute, as shown above).