I have a Flask app and it has a before_first_request method defined. The method loads some cached data for the application. I am trying to run some unit tests, and the cached data is in the way. How can I mock the method.
#app.before_first_request
def load_caches():
print "loading caches..."
# cache loading here.
in my test file, I define a global test_client as follows:
from unittest import TestCase
from .. import application
import mock
test_app = application.app.test_client()
My test classes follow that. The issue is that my test_app loads the cache and I need to mock that in my tests.
You can manually remove hooks in your test client:
test_app = application.app.test_client()
test_app.before_first_request_funcs = []
I'm surprised nobody gave a solution to this. I figure it may be useful to someone else at least. This may be a workaround, though I found this the easiest. In my case, I was testing a standalone function, not a method.
I was banging my head on your question too. I found that installing the python undecorated library and importing it in the file executing unit tests did the trick. Then after doing that, calling the undecorated method call inside of the SetUp method (before running the test_client())Something like this:
In test_my_module.py
from my_app import app, my_module
from undecorated import undecorated
class MyTestClass(unittest.TestCase):
def setUp(self):
undecorated(my_module.my_function)
# we are doing this before anything else due to the decorator's nature
# my_function has the #before_first_request decorator.
# Other setUp code below
self.client = app.test_client()
# ...
I did not find a way to mock the function directly, but i can mock functions called within it:
#app.before_first_request
def before_first_request():
load_caches()
def load_caches():
print "loading caches..."
Related
I have a module where I instantiate an object outside of my functions, so I can reuse it:
from somewhere import client
client_obj = client()
def function_with_client1():
client_obj.foo1()
def function_with_client2()
client_obj.foo2()
I want to patch this client once so that in my tests I can reuse it across multiple tests, like so:
from unittest.mock import patch
from above import client, function_with_client1, function_with_client2
mocked_client = patch('above.client') #this doesn't work
def test_function_with_client1():
function_with_client1()
def test_function_with_client2():
function_with_client2()
The above patch doesn't work, it runs the tests with the real object instance. How can I correctly patch this in my test file once, so that the tests reuse it across all tests? I know I can decorate each test with a patch, however I want to reuse the same patched object like I do in my real module
Patching global objects is always a problem as they are initialized before patching. This can be worked around by reloading the module after patching, but the better solution is not to do the inialization globally.
You can use some kind of lazy initialization, a simple implementation would be something like this:
from somewhere import client
client_obj = None
def get_client():
global client_obj
if not client_obj:
client_obj = client()
return client_obj
def function_with_client1():
get_client().foo1()
...
Now client() is not called on importing the module, and you can mock it in your test.
so I'm writing unit tests and I'm having trouble with the setUp function. From what I've seen, it's supposed to just execute code before your function and thus I could put anything that's repetitive in there. However, this function doesn't seem to be applying the mocks I've created as patch decorators over the entire class. This is a small piece of what I want it to look like:
#patch('geomet_data_registry.layer.base.get_today_and_now', new=mocked_get_date) # noqa
#patch('geomet_data_registry.layer.base.load_plugin', new=mocked_load_plugin)
#patch('geomet_data_registry.layer.base.TILEINDEX_PROVIDER_DEF', new=mocked_tileindex) # noqa
#patch('geomet_data_registry.layer.base.STORE_PROVIDER_DEF', new=mocked_store)
class TestInitBase(unittest.TestCase):
def setUp(self):
""" Code that executes before every function. """
self.maxDiff = None
self.today_date = \
datetime.now(timezone.utc).isoformat().replace('+00:00', 'Z')
mocked_get_date.return_value = self.today_date
self.base_layer = BaseLayer({'name': 'model_gem_global'})
def test_Init(self):
expected_values = {'items': [],...
base_layer_attr = self.base_layer.__dict__
self.assertDictEqual(expected_values, base_layer_attr, msg=None)
Here I mock the date received so it doesn't mess with my tests, I mock load_plugin, which when used returns a class instance of a certain plugin, I mock TileIndex which is an ES TileIndex and I mock the store, which is a redis store. If I use the code shown above, it doesn't work. When I instantiate the class BaseLayer inside setUp, none of my mocks are used and I get:
- 'receive_datetime': '2021-11-10T12:56:07.371067Z',
? ^^^
+ 'receive_datetime': '2021-11-10T12:56:07.371131Z',
? ^^^
- 'store': <MagicMock name='mock()' id='140158154534472'>,
- 'tileindex': <MagicMock name='mock()' id='140158154534472'>,
+ 'store': <BaseStore> Redis,
+ 'tileindex': <ElasticsearchTileIndex> http://localhost:9200,
However, before you tell me maybe my paths are wrong for the mocks or something like that, I can assure you that everything works fine, because the code works if I keep everything the same, except I repeat the class instantiation in every test function. Moreover, it will work if I keep this the same, but I name the setUp for example mySetUp and call it at the beginning of the function.
Everything works this way and I've already made all my tests without using the setUp at all because I remember thinking to myself "this thing is broken I'm not risking using this function in my tests".
Thanks!
By default the mock.patch decorator is applied to each function in the class starting with test. Since setUp does not start with test it will not be patched by the class decorator by default.
You can change this by setting patch.TEST_PREFIX (https://docs.python.org/3/library/unittest.mock.html#test-prefix). If you want it to apply to setUp as well you can do
patch.TEST_PREFIX = ("test", "setUp",)
and use the class decorators as before.
The problem is that the mock.patch decorator is applied to each test function, and during setUp the patching has not been done yet.
To use the same mocks for all tests, you have to start/stop mocking in setUp/tearDown instead. This could look something like:
class TestInitBase(unittest.TestCase):
def setUp(self):
self.data_patcher = patch('geomet_data_registry.layer.base.get_today_and_now',
new=mocked_get_date)
self.data_patcher.start()
self.plugin_patcher = patch('geomet_data_registry.layer.base.load_plugin',
new=mocked_load_plugin)
self.plugin_patcher.start()
...
self.today_date = \
datetime.now(timezone.utc).isoformat().replace('+00:00', 'Z')
mocked_get_date.return_value = self.today_date
self.base_layer = BaseLayer({'name': 'model_gem_global'})
def tearDown(self):
self.data_patcher.stop()
self.plugin_patcher.stop()
...
Admittedly this is not as nice as using the decorators. You can still use the decorators for the functions that don't have to already be patched in setUp, if there are any.
As a side note: with pytest this would be a bit less messy, as you can have the setup and teardown part in the same fixture.
Basically, I'm trying to do a test for each iteration of a list of routes to check that the web pages associated with a particular function return a valid status code.
I want something along the lines of this:
import pytest
from flask import url_for
from myflaskapp import get_app
#pytest.yield_fixture
def app():
# App context and specific overrides for test env
yield get_app()
#pytest.yield_fixture
def client(app):
yield app.test_client()
#pytest.yield_fixture
def routes(app):
routes = [
'foo',
'bar',
# There's quite a lot of function names here
]
with app.app_context():
for n, route in enumerate(routes):
routes[n] = url_for(route)
# yield url_for(route) #NOTE: This would be ideal, but not allowed.
# convert the routes from func names to actual routes
yield routes
#pytest.mark.parametrize('route', routes)
def test_page_load(client, route):
assert client.get(route.endpoint).status_code == 200
I read up that you can't mix parametrize with a fixture as an argument due to something along the lines of interpretation/load/execution order, although, how is this solved in terms of 'best practice'?
I saw a solution where you can generate tests from a function directly, and that seems extremely flexible and might be along the lines of what I want Passing pytest fixture in parametrize (Although I can't use call a fixture decorated function directly, so probably not)
Although, I'm new to pytest and I'd love to see more examples of how to generate tests or perform multiple tests in an iteration with little-to-no restrictions while adhering to proper pytest styling and the DRY principle. (I know about conftest.py)
I'd prioritize versatility/practicality over proper styling if that matters. (within reason, maintainability is a high priority too)
I want to be able to reference the solution to this problem to help guide how I tackle structuring my tests in the future, but I seem to keep hitting roadblocks/limitations or being told by pytest I can't do X solution the way I would expect/want too.
Relevant Posts:
DRY: pytest: parameterize fixtures in a DRY way
Generate tests from a function: Passing pytest fixture in parametrize
Very simply solution (doesn't apply to this case): Parametrize pytest fixture
Flask app context in PyTest: Testing code that requires a Flask app or request context
Avoiding edge-cases with multiple list fixtures: Why does Pytest perform a nested loop over fixture parameters
Pytest fixtures themselves can be parameterized, though not with pytest.mark.parametrize. (It looks like this type of question was also answered here.) So:
import pytest
from flask import url_for
from myflaskapp import get_app
#pytest.fixture
def app():
app = get_app()
# app context stuff trimmed out here
return app
#pytest.fixture
def client(app):
client = app.test_client()
return client
#pytest.fixture(params=[
'foo',
'bar'
])
def route(request, app):
'''GET method urls that we want to perform mass testing on'''
with app.app_context():
return url_for(request.param)
def test_page_load(client, route):
assert client.get(route.endpoint).status_code == 200
The documentation explains it this way:
Fixture functions can be parametrized in which case they will be called multiple times, each time executing the set of dependent tests, i. e. the tests that depend on this fixture. Test functions usually do not need to be aware of their re-running. Fixture parametrization helps to write exhaustive functional tests for components which themselves can be configured in multiple ways.
Extending the previous example, we can flag the fixture to create two smtp_connection fixture instances which will cause all tests using the fixture to run twice. The fixture function gets access to each parameter through the special request object:
# content of conftest.py
import pytest import smtplib
#pytest.fixture(scope="module", params=["smtp.gmail.com", "mail.python.org"])
def smtp_connection(request):
smtp_connection = smtplib.SMTP(request.param, 587, timeout=5)
yield smtp_connection
print("finalizing {}".format(smtp_connection))
smtp_connection.close()
The main change is the declaration of params with #pytest.fixture, a list of values for each of which the fixture function will execute and can access a value via request.param. No test function code needs to change.
My current solution that I fumbled my way across is this:
import pytest
from flask import url_for
from myflaskapp import get_app
#pytest.fixture
def app():
app = get_app()
# app context stuff trimmed out here
return app
#pytest.fixture
def client(app):
client = app.test_client()
return client
def routes(app):
'''GET method urls that we want to perform mass testing on'''
routes = ['foo', 'bar']
with app.app_context():
for n, route in enumerate(routes):
routes[n] = url_for(route)
return routes
#pytest.mark.parametrize('route', routes(get_app()))
#NOTE: It'd be really nice if I could use routes as a
# fixture and pytest would handle this for me. I feel like I'm
# breaking the rules here doing it this way. (But I don't think I actually am)
def test_page_load(client, route):
assert client.get(route.endpoint).status_code == 200
My biggest issue with this solution is that, I can't call the fixture directly as a function, and this solution requires either that, or doing all the work my fixture does outside of the fixture, which is not ideal. I want to be able to reference this solution to tackle how I structure my tests in the future.
FOR ANYONE LOOKING TO COPY MY SOLUTION FOR FLASK SPECIFICALLY:
My current solution might be worse for some people than it is for me, I use a singleton structure for my get_app() so it should be fine if get_app() is called many times in my case, because it will call create_app() and store the app itself as a global variable if the global variable isn't already defined, basically emulating the behavior of only calling create_app() once.
I am trying to create tests involving sending GET requests to my API using pytest-django and I need a function used in the views to be mocked.
I have tried mocker from pytest-mock and unittest.mock.patch and every time I mock this function in some test case it remains mocked in the other tests as well.
First .py test file:
from unittest.mock import patch
from rest_framework.test import APITestCase
import pytest
#pytest.mark.django_db
class TestFirst(APITestCase):
#classmethod
def setUpClass(cls):
cls.patcher = patch(app.views.function)
cls.patcher.start()
#classmethod
def tearDownClass(cls):
cls.patcher.stop()
def test_something(self):
get_data = self.client.get('/some/url')
self.assertEqual(200, get_data.status_code)
and then followed by a test in some completely different .py file:
from rest_framework.test import APITestCase
import pytest
#pytest.mark.django_db
class TestSecond(APITestCase):
def test_something_else(self):
get_data = self.client.get('/some/url')
self.assertEqual(200, get_data.status_code)
When debugging the first test case, the method is patched correctly. However when running the second test, the method remains patched and the mock object keeps the number of calls received.
Am I missing something important?
EDIT: I tried both patching the file where the method is defined and name of the method in views, but always keep getting same result.
EDIT2: Worth noting that when I change the order of the tests, the second one completes correctly, but the first one is unable to have the method patched and calls it unpatched, therefore fails.
I resolved the issue by using the SimpleTestCase superclass. I have still no idea why this was happenning, but doesn't seem to be anymore.
I've read all I can find with regards to Python 3 and mocking. Unfortunately, I still can't figure out why I need to verify assertions on mock() instead of mock when all documentation I've read use the latter.
Here is what I test looks like -
from unittest.mock import Mock, patch
from unittest import TestCase, skip
from suds.client import Client, ServiceDefinition
from run import WebService
import unittest
#patch('run.Client')
def test(self, mock):
service = WebService()
weather_id = 1234
weather = service.get_weather(weather_id)
mock().service.GetWeather.assert_called_once_with(weather_id)
run in this case is where WebService resides and Client is the suds client.
Printing mock.mock_calls I see -
[
call('SERVICE_WSDL', proxy={'https': 'PROXY', 'http': 'PROXY'}),
call().factory.create('AuthHeader'),
call().set_options(soapheaders=<MagicMock name='Client().factory.create()' id='37553264'>),
call().service.GetWeather(1234, '', '')
]
Mind you, my test passes. I'm simply wondering what I'm missing so I can better understand mocking in Python.
First of all, let's rename the variable because it's actually a mocked instance of run.Client:
#patch('run.Client')
def test(self, mock_client):
# ...
mock_client().service.GetWeather.assert_called_once_with(weather_id)
You're creating an instance of Client within run and you're using that in your code. But you're not actually mocking that instance in this test, you're mocking the class (run.Client is being patched).
So we have a mocked class and we're calling that class to create an instance. That instance is what your code actually uses. This means what you really want is access to the return value of the constructor of the class:
mock_client.return_value.service.GetWeather.assert_called_once_with(weather_id)
This is the same as your code except without calling mock_client(). Your current test code does something similar: call the mocked class, see what instance you get back and perform assertions on that. Yet the mock library provides the return_value attribute to do that already.