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).
Related
How do you make Python's unittest.mock.patch return an object that lets you assign a callable return value?
For example, I have a custom class in myclass.py defined as:
class MyClass:
#property
def someprop(self):
return 'you should never see this in a test'
I want to test a function that acts on data retrieved from someprop. In my real application, someprop actually calls some complicated external database that's not accessible in a unittest, and isn't really necessary for the purposes of the unittest, so I decide to mock a return value using the patch and the faker package.
So my unittest looks like:
import unittest
import unittest.mock
from faker import Faker
from myclass import MyClass
class Tests(unittest.TestCase):
#unittest.mock.patch('myclass.MyClass.someprop')
def test_mock_error(self, mock_myclass_someprop):
class RandomText:
#property
def text(self):
factory = Faker()
return factory.text()
# Make calls to someprop return random text.
mock_myclass_someprop.return_value = RandomText.text
a = MyClass()
actual_text = a.someprop
print('actual text:', actual_text)
self.assertTrue('MagicMock' not in str(actual_text)) # this fails
if __name__ == '__main__':
unittest.main()
Every time the test runs, the patch causes it to access the text property on my RandomText instance instead of someprop, which should return a unique string. However, this fails because the mock is actually returning a value like <MagicMock name='someprop' id='140126618001360'>.
Why is this, and how do I fix it?
I've tried refactoring how I set and call return_value, but no matter what I do, it returns a MagicMock instance instead of a real return value retrieved from my patched callable.
Is it possible to have base classes that define tests in tornado that are themselves not run as tests?
Let's say I have the following minimal example as a base class:
from tornado.testing import AsyncTestCase, gen_test
from tornado.httpclient import HTTPRequest
class AbstractTestCase(AsyncTestCase):
def set_parameters(self):
#Set some parameter value here: self.uri = ...
raise NotImplementedError
#gen_test
def test_common_functionality(self):
req = HTTPRequest(self.uri, method = "GET")
response = yield self.client.fetch(req, raise_error=False)
self.assertEqual(200, response.code)
Now, I would like to make several test cases that inherit from this, define their own value for self.uri...and have some specific tests of their own. Like this:
class ConcreteTestCase(AbstractTestCase):
def set_parameters(self):
self.uri = "www.stackoverflow.com"
#gen_test
def test_some_other_thing(self):
self.assertEqual(2, 1+1)
However, when I try to run this, the AbstractTestCase is also run on its own, giving an error (the NotImplementedError). This happens even when I only try to run the inheriting tests.
Is there any way around this issue, or do I have to duplicate the functionality in each test case?
One way to do this is with multiple inheritance. The abstract class doesn't need to extend AsyncTestCase as long as that class is in the inheritance hierarchy at runtime.
class AbstractTestCase(object):
def set_parameters(self):
#Set some parameter value here: self.uri = ...
raise NotImplementedError
#gen_test
def test_common_functionality(self):
req = HTTPRequest(self.uri, method = "GET")
response = yield self.client.fetch(req, raise_error=False)
self.assertEqual(200, response.code)
class ConcreteTestCase(AbstractTestCase, AsyncTestCase):
def set_parameters(self):
self.uri = "www.stackoverflow.com"
#gen_test
def test_some_other_thing(self):
self.assertEqual(2, 1+1)
This is admittedly a little weird and mypy type checking doesn't like it. But it's simple and it works, and I haven't found a mypy-friendly alternative that I like.
CLI
python3 -m tornado.testing ConcreteTestCase.ConcreteTestCase
testmain.py
#!/usr/bin/env python3
import unittest
from tornado.testing import main
def all():
cases = ['ConcreteTestCase.ConcreteTestCase']
return unittest.defaultTestLoader.loadTestsFromNames(cases)
main()
test/runtests.py is a good example.
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.
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'm attempting to create a few unit tests for my class. I want to mock these, so that I don't burn through my API quota running some of these tests. I have multiple test cases that will call the fetch method, and depending on the passed URL I'll get different results back.
My example class looks like this:
import requests
class ExampleAPI(object):
def fetch(self, url, params=None, key=None, token=None, **kwargs):
return requests.get(url).json() # Returns a JSON string
The tutorial I'm looking at shows that I can do something like this:
import unittest
from mock import patch
def fake_fetch_test_one(url):
...
class TestExampleAPI(unittest.TestCase):
#patch('mymodule.ExampleAPI.fetch', fake_fetch_test_one)
def test_fetch(self):
e = ExampleAPI()
self.assertEqual(e.fetch('http://my.api.url.example.com'), """{'result': 'True'}""")
When I do this, though, I get an error that says:
TypeError: fake_fetch_test_one() takes exactly 1 argument (3 given)
What is the proper way to mock a requests.get call that is in a method in my class? I'll need the ability to change the mock'd response per test, because different URLs can provide different response types.
Your fake fetch needs to accept the same arguments as the original:
def fake_fetch(self, url, params=None, key=None, token=None, **kwargs):
Note that it's better to mock just the external interface, which means letting fetch call requests.get (or at least, what it thinks is requests.get):
#patch('mymodule.requests.get')
def test_fetch(self, fake_get):
# It would probably be better to just construct
# a valid fake response object whose `json` method
# would return the right thing, but this is a easier
# for demonstration purposes. I'm assuming nothing else
# is done with the response.
expected = {"result": "True"}
fake_get.return_value.json.return_value = expected
e = ExampleAPI()
self.assertEqual(e.fetch('http://my.api.url.example.com'), expected)
from you test method you can monkeypatch your requests module
import unittest
class Mock:
pass
ExampleAPI.requests = Mock()
def fake_get_test_one(url):
/*returns fake get json */
ExampleAPI.requests.get= Mock()
ExampleAPI.requests.json = fake_get_test_one
class TestExampleAPI(unittest.TestCase):
def test_fetch(self):
e = ExampleAPI()
self.assertEqual(e.fetch('http://my.api.url.example.com'), """{'result': 'True'}""")
you can setup the patch in each setup() and corresponding teardown() methods of your test class if needed