Unit testing a flask application: how to handle AssertionErrors? - python

Here is my test_server.py file:
from flask_testing import TestCase
class TestServer(TestCase):
def create_app(self):
import server
app = server.run()
return app
def test_1(self):pass
def test_2(self):pass
server.run does many things and ends by returning the app:
import flask
app = flask.Flask(__name__)
def run():
#many things that don't seem related to the issue
import views
views.run(app=app)
return app
views.run declares many of the the #app.route endpoints. Here is a minimal case:
import flask
def run(app):
#app.route("/", methods=['GET'])
def index():
return flask.render_template('index.html')
If I comment one of the two tests, everything goes fine. If I leave both of them I get:
AssertionError: View function mapping is overwriting an existing endpoint function: index
From what I understand about how this works the problem is caused because views.run is called twice. However I don't see any reason why it is the same instance that is used for both calls.
When tested "by hand" the server works fine, the problem only comes when using unit tests. The code above uses flask_testing but I had the same issue sooner with unittest.
What am I doing wrong?

Related

How to use use flask-caching decorator #cache on an external module class

Context:
I have flask application, which is an NGINX authentication module, written in flask, and in some part of the code I am making a call to an LDAP server.
The objective is to use flask-caching library to cache the response from the LDAP server and avoid that costly call.
The code bellow its a very stripped down version showing only the relevant sections.
Problem
In the below scenario I can't use the decorator #cache.memoized on a method of the Ldap class, as the variable cache is not available in that module.
name 'cache' is not defined
main.py
from flask import Flask
from flask_caching import Cache
from ldap import Ldap
app = Flask(__name__)
cache = Cache(app)
#app.route('/', defaults={'path': ''})
#app.route('/<path:path>')
#auth.login_required
def index(path):
code = 200
msg = "Another LDAP Auth"
headers = [('x-username', getRegister('username')),
('x-groups', getRegister('matchedGroups'))]
return msg, code, headers
#auth.verify_password
def login(username, password):
ldap = Ldap()
ldap.verifyUser(username, password)
ldap.py
class Ldap:
#cache.memoized(timeout=300) <<<< error
def validateUser(self, username, password)
if <ldap query goes here>
return True
return False
Research
The weird thing for me here is that this decorator depends on an object instance and not on a class as so many other scenarios I've seen
Alternative:
Definitively if I put the definition of the class in the same main.py and bellow the definition of the cache variable it works, however this will make my main.py file too long.
Attempt 1:
Trying to import my module after the definition of the cache variable has the same error
Attempt 2:
doing from main import cache inside ldap.py creates a circular import error.
Idea:
Pass the cache variable to the Ldap Class constructor and "somehow" use it to decorate the method, but I could not find how to do it exactly.

How to always provide a context for Flask app tested with PyTest?

I try to implement unit tests with Pytest on a Flask app and I have a hard time to do it.
My Flask application uses configuration files on most of the functions (here some_method) to illustrate. So it seems that I should provide a context for each call to any method that I would like to test. It seems that I can achieve it with "with app.app_context():" on each call.
I read the official testing documentation but they talk about creating a client. As I would like to do unit tests, I need to call sub functions which are not top level.
Is there a way to always provide a context without pushing the context manually on each call?
Please find below my current implementation:
main.py
from flask import current_app
def main(request):
current_app.config.from_envvar('APPLICATION_SETTINGS')
print(some_method())
return 'OK'
def some_method():
# doing some stuff using the context
world = current_app.config['SECRET_KEY']
return world
test_main.py
import pytest
from flask import current_app, Flask
from main import main, some_method
#pytest.fixture
def app():
app = Flask(__name__)
# load here any potential configuration configuration
return app
def test_some_method(app):
with app.app_context():
# calling and doing some assertion
some_method()
PS: I do not have app = Flask(name) in my main file because I am running on the Functions Framework
pytest-flask seems to configure the context on any call.
conftest.py
import pytest
from flask import Flask
#pytest.fixture
def app():
app = Flask(__name__)
return app
test_main.py
import pytest
from flask import current_app, Flask
from main import main, some_method
def test_some_method(app):
#with app.app_context():
# calling and doing some assertion
some_method()
works.

How to put decorators on `app` when using Application Factory with Flask?

I'm trying to define some global constants for my app, and have found that this can be done with a function that's decorated as #app.context_processor.
However, the issue is that I don't have an app variable. My application uses an application factory, and I'd like to keep it that way. Is there a different way to register a function as the context_processor for my app?
One option I have seen is to apply the decorator to each Blueprint instead of applying it to the app. That's something I would like to avoid though, since it would lead to a lot of duplicate code.
The issue is that there is no app object in case of factories. You have a create_app function where the app gets created.
So to install the context processors you can use create_app itself
def create_app(config_filename):
app = Flask(__name__)
app.config.from_pyfile(config_filename)
from yourapplication.model import db
db.init_app(app)
from yourapplication.context_processor import myprocessor
app.context_processor(myprocessor)
from yourapplication.views.frontend import frontend
app.register_blueprint(frontend)
return app
You could also have the function in the same app.py file (wherever the create_app() function is written). In such a case, you could simply register the context_processor without importing it.
Another approach is to do it in a blueprint as shown in below
Flask context processors functions
from flask import Blueprint
thingy = Blueprint("thingy", __name__, template_folder='templates')
#thingy.route("/")
def index():
return render_template("thingy_test.html")
#thingy.context_processor
def utility_processor():
def format_price(amount, currency=u'$'):
return u'{1}{0:.2f}'.format(amount, currency)
return dict(format_price=format_price)

Flask testing create_app is not returning the application?

I am working on setting up unit tests in my flask project right now. My test file is below:
import flask_testing
import unittest
from flask import Flask
from flask_testing import TestCase
class MyTest(TestCase):
def setUp(self):
pass # Executed before each test
def tearDown(self):
pass # Executed after each test
def create_app(self):
app = Flask(__name__)
# app.config['TESTING'] = True
return app
def test_greeting(self):
response = self.client.get('/')
print("should return 404 on landing page")
self.assertTemplateUsed('index.html')
self.assertEqual(response.status_code, 200)
if __name__ == '__main__':
unittest.main()
When I run these tests the assertTemplateUsed does not return a template and the response.status_code is 404. I imagine this is because self is not actually my application for some reason? When I run the app the root address definitely returns index.html.
Am I setting up create_app wrong? Any help is appreciated.
You need to create your Flask app instance in the setUp() function. At the moment the create_app() function doesn't get called at all.
Change your code like this:
import flask_testing
import unittest
from flask import Flask
from flask_testing import TestCase
class MyTest(TestCase):
def setUp(self):
self.app = Flask(__name__)
self.app_context = self.app.app_context()
self.app_context.push()
self.client = self.app.test_client(use_cookie=True)
def tearDown(self):
self.app_context.pop()
def test_greeting(self):
response = self.client.get('/')
print("should return 404 on landing page")
self.assertTemplateUsed('index.html')
self.assertEqual(response.status_code, 200)
if __name__ == '__main__':
unittest.main()
The setUp() function gets called prior to each test function. At first you will create a new instance of your Flask app. If you want to access items in your application context it is good practice to push it in your setUp() function and pop it in your tearDown() function. You can leave this out if you don' t access app_context items (like Database session objects) from your test function. At last you need to creat the test client in your setUp() function. You missed that part in your post but I guess you did it somewhere else in your code.
In your setUp function, you need to provide a test client to make the requests. Try something like this.
def setUp(self):
# this test client is what flask-testing will use to make your requests
self.app = app.test_client()
Check this out for more information How to Test a Flask Application.

How to share the global app object in flask?

I am using flask and trying to the following.
I have defined a main.py file through which I want to run my app ie python main.py -
from flask import Flask
from view import tags
app = Flask(__name__)
#app.route('/')
def hello_world():
return 'Hello World!'
if __name__ == '__main__':
app.run()
I have defined a package named view in which I will be declaring my different view modules, each having its own routes. view.tags.py -
from flask import Flask
app = Flask(__name__)
#app.route('/e')
def hello_world2():
return 'Hello World!'
So I need to have the global app object in my main.py for running the server, as well as in the view classes of my package for registering the routes. So how do I create the global app object and share it between all classes ?
Thanks,
Murtaza
You can import current_app from flask. It stores a reference to the global application object.
from flask import current_app as app
def home():
return render_template('base.html', name=app.name)
First, I would suggest to take a look at Blueprints http://flask.pocoo.org/docs/blueprints/ This will help to organize the app easily.
Also take a look at http://flask.pocoo.org/docs/api/#flask.current_app flask.current_app, the way how to get your app instance in other modules.
This link also could be helpful on how to organize and build flask app (it is not ideal for sure, but can give you some ideas) - Large-app-how-to.md
Have fun :)
One way is to create an overall package and adding a __init__.py file under that where you declare all global variables. In your case for example, you can create something like:
myapplication/
* __init__.py
* myviews/
* __init__.py
* view.py
* tags.py
etc
Now you add the following code in the __init__.py file:
app = Flask(__name__)
You can now use this app variable anywhere as long as you import the package myapplication.
import myapplication.myviews.view
Just import it from your other files. Perhaps the best way to do this is to put your app object in one single file and have everything else import from it.
For example, your main.py could still have:
from flask import Flask
from view import tags
app = Flask(__name__)
And then in other files, you could do:
from .main import app
or, if outside your package, just use the complete string
from mypackagename.main import app
One thing to be careful of is circular imports. The easiest way to handle this issue is to create your app first and then import whatever else you need to from your base file after you create it.
So for example:
from flask import Flask
app = Flask(__name__)
# do some stuff with app
from .views import view1, view2
from .server import run
So long as you put the imports after you've created app, like the above, you shouldn't have an issue with circular imports.
Regarding import and use of current_app from flask in a "helper" python function in a separate source file, this works as long as a current app context has already been set up (E.g. web request received). I have a case where, during application initialization (app.run not yet invoked), app.logger is invoked in the helper function.
Before I fixed it (see below), I got a stack trace punctuated by "RuntimeError: Working outside of application context".
Sample solution:
main.py:
import helper
...
app = Flask(__name__.split('.')[0],
template_folder="templates",
static_folder="static")
...
# Fix: Create an app context
with app.app_context():
helper.dbopen(...)
...
app.run(...)
helper.py:
from flask import current_app as app
...
def dbopen():
app.logger.info(...)
...
If you have a file AppName.py in which you define app, and then you have another file Foobar.py that needs it, you can always say in AppName.py:
import Foobar
Foobar.app = app
Then in Foobar.py you should be able to use app in your functions. One thing you want to be careful of is that you can't have code in Foobar.py that runs immediately when the file is called the depends on the app variable which is passed in after the import.

Categories