Reputation: 5088
I have a flask application which uses a global object data_loader
.
The main flask file (let's call it main.py
) starts as follow:
app = Flask('my app')
...
data_loader = DataLoader(...)
Later on, this global data_loader
object is called in the route methods of the webserver:
class MyClass(Resource):
def get(self):
data_loader.load_some_data()
# ... process data, etc
Using unittest, I want to be able to patch the load_some_data()
method. I'm using the flask test_client:
from my_module.main import app
class MyTest(unittest.TestCase):
@classmethod
def setUpClass(cls) -> None:
cls.client = app.test_client('my test client')
How can I patch the data_loader
method in subsequent tests in MyTest
? I have tried this approach, but it does not work (although the data_loader
seems to be replaced at some point):
@unittest.mock.patch('my_module.main.DataLoader')
def my_test(self, DataLoaderMock):
data_loader = DataLoaderMock.return_value
data_loader.my_method.return_value = 'new results (patched)'
with app.test_client() as client:
response = client.get(f'/some/http/get/request/to/MyTest/route',
query_string={...})
# ... some assertions to be tested ...
It seems the data_loader
is never truly replaced in the Flask app.
Also, is this considered "good practice" to have a global variable in the Flask server, or is the app supposed to have it stored inside?
Thanks
Upvotes: 1
Views: 1712
Reputation: 11223
About mocking
, patch.object can be used to modify object attributes:
@unittest.mock.patch.object(data_loader, 'my_method')
def my_test(self, my_method_mock):
my_method_mock.return_value = 'new results (patched)'
with app.test_client() as client:
response = client.get(f'/some/http/get/request/to/MyTest/route',
query_string={...})
my_method_mock.assert_called() # ok!
My solution with interesting insights would be:
import unittest
from unittest.mock import patch
class MyTest(unittest.TestCase):
def test_get(self):
client = app.test_client('my test client')
patcher = patch('{package_here}.{module_here}.DataLoader.load_some_data', return_value={'status': 1})
patcher.start()
self.assertDictEqual(client.get('/').json, {'status': 1})
patcher.stop()
# or
with patch('{package_here}.{module_here}.DataLoader.load_some_data', return_value={'status': 1}):
self.assertDictEqual(client.get('/').json, {'status': 1})
About "good practice" and global variables. Yes, I have seen global variables in various projects. But I don't recommend using global variables. Because:
Flask
application with recursive imports. It is really pain. And you can't fix all problems for a short time.mocking
a global variables. I think refactoring is more difficult when you have a rather big service.import all dependencies
-> load config
-> initialization
-> run
. In other case you will have import -> new instance -> new instance -> import -> ...
.Maybe global variables is not bad way for a stand alone packages
, modules
etc but not for a project. I also want to recommend using some additional tools. This will not only make it easier to write tests, but it will also save you headaches.
Upvotes: 1