Mock an internal object call in a Flask view - with pytest - flask

Based on the following example:
app = Flask(__name__)
#app.route('/users')
def get_users():
return UsersAPI().get_users()
And the following tests (using pytest and pytest-mock):
#pytest.fixture
def users():
return UsersAPI(how_many=1)
def test_simple(users, mocker):
mocker.patch("???", return_value=users)
I simply want to call UsersAPI(how_many=1) instead of UsersAPI(). Is this possible to do?
(if you know how to get done with unittest.mock that is also fine since pytest-mock is simply some pytest wrapper)

Turns out it is as easy as:
#pytest.fixture
def users():
return UsersAPI(how_many=1)
def test_simple(users, mocker):
mocker.patch("path.to.module.UsersAPI", return_value=users)
And this also works:
mocker.patch.object(path.to.module, 'UsersAPI', return_value=users)

Related

How to mock method from mocked library instance

Folks,
I have following situation:
tested_library:
class foo(object):
def __init__(self):
self.bar = BuiltIn().get_library_instance('bar')
def get_status(self):
trace_out = self.bar.some_method_from_mocked_lib()
trace = re.findall(r'([A-Z\d]+)\s*(on|off)', trace_out )
Where BuiltIn is a robotframework library which is not working without robot's environment and python is not able to import this library because of BuiltIn and I had to mock it (below). trace is still used later and method get_status() returns something else.
test:
import pytest
from _pytest.monkeypatch import MonkeyPatch
from mock import MagicMock
monkeypatch = MonkeyPatch()
def import_lib(monkeypatch):
monkeypatch.setattr('robot.libraries.BuiltIn.BuiltIn.get_library_instance', MagicMock())
import tested_library as lib
return lib.foo
def test_getting_status():
test_object = import_lib(monkeypatch)
test_object.get_status()
But returns me:
pattern = '([A-Z\\d]+)\\s*(on|off)', string = <MagicMock name='mock().some_method_from_mocked_lib()' id='140623304433104'>, flags = 0
And the question is how to mock this, how to return string not an mocked object with method?

How to mock redis for Django tests

I am trying to mock out redis in my Django application. I have tried several different methods but none seem to work. What am I doing wrong?
My primary redis instance is called with:
redis_client = redis.from_url(os.environ.get("REDIS_URL"))
That instance is imported in other parts of the app in order to add and retrieve data.
In my tests I tried doing:
import fakeredis
from mock import patch
class TestViews(TestCase):
def setUp(self):
redis_patcher = patch('redis.Redis', fakeredis.FakeRedis)
self.redis = redis_patcher.start()
self.redis.set('UPDATE', 'Spring')
print(redis_client.get('UPDATE'))
def tearDown(self):
self.redis_patcher.stop
When running the tests I want the 'UPDATE' variable to be set. But instead every instance of redis_client fails saying the server is not available. How can I mock out redis and set values, so that they are available when testing my app?
You should mock an item where it is used, not where it came from.
So if redis_client is used in a view like this:
myapp/views.py
from somemodule import redis_client
def some_view_that_uses_redis(request):
result = redis_client(...)
Then in your TestViews you should patch redis_client like this:
class TestViews(TestCase):
def setUp(self):
redis_patcher = patch('myapp.views.redis_client', fakeredis.FakeRedis)
self.redis = redis_patcher.start()

how to mock a method decorated with Python Flask route()

I need to unit test whether a method decorated by a Flask route() gets called or not.
I'd like to do this without modifying the original code under test, if possible, so mocking the method would suite my requirements perfectly.
Hence I am asking this specific question about how to mock a decorated request method (I want to stress this to try to avoid people wasting their time with less specific answers)...
Sample application jflask.py:
from flask import Flask
app = Flask(__name__)
app.config.from_object(__name__)
#app.route('/hello') # This method represents the code under test.
def hello(): # I want to assert that this method gets
return 'Hello, World' # called without modifying this code.
if __name__ == "__main__":
app.run()
In the unit test I'm using #patch() to mock the method so I can assert it was called, but the assertion fails. I.e. the mock method doesn't get called, when I expect it to.
Sample unit test test_hello.py:
import unittest
import jflask
from unittest.mock import patch
class jTest(unittest.TestCase):
def setUp(self):
#jflask.app.testing = True
self.app = jflask.app.test_client()
#patch('jflask.hello') # mock the hello() method
def test_hello(self, mock_method):
rv = self.app.get('/hello')
mock_method.assert_called() # this assertion fails
What am I doing wrong ?
Background
Some background information about the actual behaviour I'm trying to test
(since the above is just a condensed test case, and may not seem entirely sane by itself).
In the actual code I am unit testing, there is a before_request() handler
installed for the app. This gets called by Flask before each request is handled, and in
certain situations this handler has been designed to return a response value, which
causes Flask request processing to stop (in this application's case, this feature is used to centrally validate request parameters), so that the usual routed request handler will (deliberately) not get called.
My unit tests need to assert that request processing gets stopped
or continues, appropriately depending on the situation.
Hence, my test needs to mock the real request handler and assert whether
it was called or not.
This is a little hacky but you could inject a logger.
#app.route(...):
def hello(logger=None):
logger = logger or self.logger
logger.info(...)
return ...
def test_...(self):
logger = MagicMock()
self.app.get(logger)
self.assertTrue(logger.info.called)
from functools import wraps
import logging
from datetime import datetime
logging.basicConfig(filename=datetime.now().strftime('%d_%m_%Y.log'),level=logging.INFO)
def logger_required(f):
#wraps(f)
def decorated(*args, **kwargs):
logging.info(f.__name__ + ' was called')
return f(*args, **kwargs)
return decorated
#app.route('/hello')
#logger_required
def hello(): # I want to assert that this gets called
return 'Hello, World'

Functional Test Not isolating from database

I am having a problem where my functional test is not isolating from the main database, there is data left over from the test. I ran python manage.py flush and the test works, but when I run the test again the data created by the test that should have been deleted is still there making my test fail. I am currently only using SQLite.
functional_test.py
from django.test import LiveServerTestCase
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
class NewUserTest(LiveServerTestCase):
def setUp(self):
self.browser = webdriver.Firefox()
self.browser.implicitly_wait(15)
def tearDown(self):
self.browser.quit()
def test_user(self):
#Some test
You forgot to indent your method definitions (the statements that begin with "def"). As a result, the Python interpreter thinks that you've defined a new class, called NewUserTest, that contains no attributes, and three top-level functions.
To solve the problem, simply indent the "def" statements by an appropriate amount (4 spaces is the standard convention amongst the Python community). It should look like this:
class NewUserTest(LiveServerTestCase):
def setUp(self):
self.browser = webdriver.Firefox()
self.browser.implicitly_wait(15)
def tearDown(self):
self.browser.quit()
...

Using webapp2 i18n in unit tests

I'm using webapp2 with webapp2_extras.i18n for a Google App Engine app.
I have a unit test script as described on the bottom here: https://developers.google.com/appengine/docs/python/tools/localunittesting
The test script imports the models and does not include webapp2 handlers, because the target of the test is the business logic code, not the requests and responses. However, some of my models will call i18n functions like format_currency or gettext which will result in an error:
AssertionError: Request global variable is not set.
How can I initialize the i18n module without instantiating a webapp2 app and request?
I had the same problem (but for uri_for) and I ended up doing the following in my test :
app = webapp2.WSGIApplication(
[webapp2.Route('/', None, name='upload_handler')])
request = webapp2.Request({'SERVER_NAME':'test', 'SERVER_PORT':80,
'wsgi.url_scheme':'http'})
request.app = app
app.set_globals(app=app, request=request)
# call function that uses uri_for('upload_handler')
I had to do trial and error to guess which environment variables have to be set in the request. Maybe you'll need to add more in order to call i18n.
Try to mock your functions.
Example: I have a script called users that import i18n like this:
from webapp2_extras.i18n import gettext as _
So on my tests i mock the function like this:
from pswdless.model import PswdUserEmail, EmailUserArc
from pswdless.users import FindOrCreateUser
from pswdless import users
# mocking i18n
users._ = lambda s: s
#your tests bellow
You can use same trick with another functions.
I hope it helps you.
It seems quite simple to mock i18n itself. I'd prefer this approach because Request and app are really not needed in unit-tests.
Here's a sample pytest fixture:
#pytest.fixture
def mock_i18n(monkeypatch):
class MockI18n:
def set_locale(self, locale):
pass
def gettext(self, string, **variables):
return string
mock_i18n = MockI18n()
def mock_get_i18n(factory=None, key=None, request=None):
return mock_i18n
from webapp2_extras import i18n
monkeypatch.setattr(i18n, 'get_i18n', mock_get_i18n)
yield
Mocking does seem to be the way to go here, but the other answers are not complete and/or more complicated than necessary. Here is a simple mock that works for me.
=== my_module.py ===
from webapp2_extras.i18n import gettext as _
def f(x):
return _(x)
=== test_my_module.py ===
import my_module
def _mock(x):
return x
#mock.patch("my_module._", side_effect=_mock)
def test_f(self, foo):
y = my_module.f("hello")
self.assertEqual(y, "hello")