Templates when unit testing a Flask application - python

I'm writing unit tests for a Flask application that has roughly the following organization:
/myapplication
runner.py
/myapplication
__init__.py
/special
__init__.py
views.py
models.py
/static
/templates
index.html
/special
index_special.html
/tests
__init__.py
/special
__init__.py
test_special.py
In particular, I want to test that the special module works as expected.
I have defined the following:
in special/views.py:
mod = Blueprint('special', __name__, template_folder="templates")
#mod.route('/standard')
def info():
return render_template('special/index_special.html')
in myapplication/__init__.py:
app = Flask(__name__)
def register_blueprints(app):
from special.views import mod as special_blueprint
app.register_blueprint(special_blueprint, url_prefix='/special')
register_blueprints(app)
in myapplication/tests/test_special.py
class TestSpecial:
#classmethod
def create_app(cls):
app = Flask(__name__)
register_blueprints(app)
return app
#classmethod
def setup_class(cls):
cls.app = cls.create_app()
cls.client = cls.app.test_client()
def test_connect(self):
r = self.client.get('/standard')
assert r.status_code == 200
While the application itself works fine, the test_connect unit-test fails with a TemplateNotFound: special/index_special.html exception.
How could I tell the tests where to find the corresponding templates ? Bypassing the rendering of templates using Flask-testing is not really an option...

You can pass template_folder to the application object constructor:
app = Flask(__name__, template_folder='../templates')
You may have to use an absolute path, I'm not sure.
http://flask.pocoo.org/docs/api/#flask.Flask
I mostly tend to have a create_app function with my application code and use that in my tests, just so the application object is consistent. I'll only create a separate app if I want to test a single blueprint or something small in isolation.
def create_app(conf_obj=BaseSettings, conf_file='/etc/mysettings.cfg'):
app = Flask(__name__)
app.config.from_object(conf_obj)
app.config.from_pyfile(conf_file, silent=True)
.... blueprints etc
return app
Then in my tests:
class TestFoo(unittest.TestCase):
def setUp(self):
self.app = create_app(TestSettings)
....

Related

Flask Tutorial __init__.py content

I'm a beginner in Python and flask. I am going through the Flask tutorial up to Blog Blueprint section.
I would like to know the meaning of app = ...
int the following code:
def create_app():
app = ...
# existing code omitted
from . import blog
app.register_blueprint(blog.bp)
app.add_url_rule('/', endpoint='index')
return app
In a real Flask application ... would be replaced by a call to the Flask constructor, with the desired configurations.
Check this example on how to initialize a Flask app: https://flask.palletsprojects.com/en/2.1.x/tutorial/factory/
def create_app(test_config=None):
# create and configure the app
app = Flask(__name__, instance_relative_config=True)
app.config.from_mapping(
SECRET_KEY='dev',
DATABASE=os.path.join(app.instance_path, 'flaskr.sqlite'),
)
if test_config is None:
# load the instance config, if it exists, when not testing
app.config.from_pyfile('config.py', silent=True)
else:
# load the test config if passed in
app.config.from_mapping(test_config)
# ensure the instance folder exists
try:
os.makedirs(app.instance_path)
except OSError:
pass
# a simple page that says hello
#app.route('/hello')
def hello():
return 'Hello, World!'
return app

Flask python app failing to get app object after initializing

app_instance.py
from app import FlaskApp
app = None
def init_instance(env):
global app
app = FlaskApp(env)
def get_instance():
assert app is not None
return app
FlaskApp class is pretty much like this
class FlaskApp(object):
def __init__(self, env):
self.oauth_manager = .... bla bla ..
self.clients_manager = .. bla bla ..
app = Flask(__name__)
app.config.from_object(env)
app = app_wrapper.wrap(app, app.config['NUM_PROXY_SERVERS'])
self.app = app
self.api = Api(self.app, prefix='/v3', default_mediatype='application/json')
self.define_routes()
# Initialize the DB
self.db = Database(self.app)
fmt = "%(asctime)s - %(request_id)s - %(name)s - %(levelname)s - %(message)s"
logging.basicConfig(format=fmt, level=self.app.config.get('LOG_LEVEL'))
request_id.init(app, prefix='MY_API_', internal=False)
def run_server(self):
self.app.run(host=self.app.config['HOST'], port=self.app.config['PORT'], debug=self.app.config['DEBUG'])
def define_routes(self):
# Configure Api Resources
self.api.add_resource(VersionListController, '/my/route', endpoint='versions')
more routes here
self.api.init_app(self.app)
in my app controller
def is_valid_oauth_token(request):
from mobile_module import app_instance
app = app_instance.get_instance()
# more code here
I'm running the app on localhost and getting
assert app is not None
AssertionError
How can "fix" this code? should I be importing from mobile_module import app_instance in every route access? suggestions please
I should state that this app works in production well behind a Nginx
I guess my question is more about python (how to make this work) and less in flask.
The problem is not related to get_instance or init_instance(create_app etc.).
Flask has different states. App will work in out of request context when you initialize app instance(FlaskApp(env)).
As I see in your example, you try to get a application in context of request(def is_valid_oauth_token(request)). It means that is not initialization of application. This is processing while a request is active. This is other state of application - app was created and work in context of some request. In this case you can get application instance using from flask import current_app.
To better understanding how it works/use I recommend to read about flask._app_ctx_stack, app_context() and flask.g.
Hope this helps.
I think the best way to devellop a flask app simple is to follow the official documentation about simple flask project structure here
you need to organize your floder like this :
/yourapplication
/yourapplication
__init__.py
/static
style.css
/templates
layout.html
index.html
login.html
...
and then create your application in init.py file like as follow:
from flask import Flask
def create_app():
"""this method will initialise the flask Ap instance """
app = Flask(__name__)
#app.route('/')
def hello_world():
return 'Hello, World!'
return app
in your yourapplication directory add the run.py to run the app with the following code :
from yourapplication import create_app
app = create_app()
if __name__ == '__main__':
app.run()
and if you want to use your controller you can do this:
from yourapplication import create_app
def is_valid_oauth_token(request):
app = create_app()
# more code here
this is called application factory design-pattern.
And in addition if you want to put it in production you will need to use WSGI configuration find more here

Flask blueprint doesn't work without prefix

Hi I have a Flask app structured in following way and I have problem with blueprints setup. Whatever I do, they only work with url_prefix set up. It works currently as /main/verify but as it is a small app I would love to have an endpoint like /verify. What's interesting I managed to make it work with / route, but for the same configuration it didn't work for the /verify. I am pretty clueless right now, I can live with it as it is, but I really wonder what am I doing wrong.
Here is the code:
__init__.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from config import config
db = SQLAlchemy()
def create_app(config_name):
app = Flask(__name__)
app.config.from_object(config[config_name])
config[config_name].init_app(app)
db.init_app(app)
from main import main
app.register_blueprint(main)
return app
main/__init__.py
from flask import Blueprint
main = Blueprint('main', __name__, url_prefix='/main')
from . import views
main/views.py
from flask import request, jsonify
from . import main
#main.route('/')
def index():
return "Hello world"
#main.route('/verify')
def verify():
url = request.args['url']
query = request.args['query']
return jsonify({ ... })
As I see you didn't register blueprint without prefix. If you need to register endpoints without prefix you must create a new instance of Blueprint
main = Blueprint('main', __name__, url_prefix='/main')
# main endpoints(with prefix /main)...
#main.route('/')
def index_main():
return "Hello world from /main/"
# routes without any prefix
default = Blueprint('default', __name__)
#default.route('/')
def index():
return "Hello world from /"
app = Flask(__name__)
app.register_blueprint(main)
app.register_blueprint(default)
Hope this helps.

Flask returns 404 in views

I am running unittests in a Flask app and I keep getting 404 when views.py file is not imported even though it is not used. I have such tests.py package:
import unittest
from presence_analyzer import main, utils
from presence_analyzer import views
class PresenceAnalyzerViewsTestCase(unittest.TestCase):
def setUp(self):
self.client = main.app.test_client()
def test_mainpage(self):
resp = self.client.get('/')
self.assertEqual(resp.status_code, 302)
When I delete views import the described problem occurs. Views are organized in a similar way to this:
from presence_analyzer.main import app
#app.route('/')
def mainpage():
return redirect('/static/presence_weekday.html')
And the main.py file:
import os.path
from flask import Flask
app = Flask(__name__) # pylint: disable=invalid-name
app.config.update(
DEBUG=True,
)
I guess it's something similar to what happened in this case, so I'm trying to change the application so that I don't have to make this dumb imports while testing. I've been trying to make use of the answer from above, but still can't make it work and these docs don't seem helpful. What am I doing wrong? main.py:
from flask.blueprints import Blueprint
PROJECT_NAME = 'presence_analyzer'
blue_print = Blueprint(PROJECT_NAME, __name__)
def create_app():
app_to_create = Flask(__name__) # pylint: disable=invalid-name
app_to_create.register_blueprint(blue_print)
return app_to_create
app = create_app()
views.py:
from presence_analyzer.main import app, blue_print
#blue_print.route('/')
def mainpage():
return redirect('/static/presence_weekday.html')
And tests.py has remained unchanged.
You must import views, or the route will not be registered. No, you are not executing the views directly, but importing executes code all module-level code. Executing code calls route. route registers the view function. You cannot get around needing to import a module in order to use the module.

404 Response when running FlaskClient test method

I'm baffled by this. I'm using an application factory in a Flask application and under the test configuration my routes always return 404s.
However when I use Flask-Script and load the app from the interpreter everything works as expected, the response comes back as 200.
Navigating to the URL with the browser works fine
app/__init__.py
def create_app():
app = Flask(__name__)
return app
sever1.py
from flask import Flask
from flask_script import Manager
from app import create_app
app = create_app()
app_context = app.app_context()
app_context.push()
manager = Manager(app)
#app.route('/')
def index():
return '<h1>Hello World!</h1>'
#app.route('/user/<name>')
def user(name):
return '<h1>Hello, %s!</h1>' % name
#manager.command
def test():
"""Run the unit tests"""
import unittest
tests = unittest.TestLoader().discover('tests')
unittest.TextTestRunner(verbosity=2).run(tests)
if __name__ == '__main__':
manager.run()
tests/test.py
#imports committed
def setUp(self):
self.app = create_app('testing')
self.app_context = self.app.app_context()
self.app_context.push()
self.client = self.app.test_client()
def test_app_exists(self):
response = self.client.get('/', follow_redirects=True)
print(response) #404 :(
self.assertTrue("Hello World!" in response.get_data()) #this is just an example of how it fails
You're not using the factory pattern correctly. You should use blueprints to collect routes and register them with the app in the factory. (Or use app.add_url_rule in the factory.) Nothing outside the factory should affect the app.
Right now you create an instance of the app and then use that instance to register routes. Then you create a different instance in your tests, which doesn't have the routes registered. Since that instance doesn't have any registered routes, it returns 404 for requests to those urls.
Instead, register your routes with a blueprint, then register the blueprint with the app in the factory. Use the factory to create an app during tests. Pass the factory to the Flask-Script manager. You should not need to push the app context manually.
from flask import Flask, Blueprint
from flask_script import Manager
from unittest import TestCase
bp = Blueprint('myapp', __name__)
#bp.route('/')
def index():
return 'Hello, World!'
def create_app(config='dev'):
app = Flask(__name__)
# config goes here
app.register_blueprint(bp)
return app
class SomeTest(TestCase):
def setUp(self):
self.app = create_app(config='test')
self.client = self.app.test_client()
def test_index(self):
rv = self.client.get('/')
self.assertEqual(rv.data, b'Hello, World!')
manager = Manager(create_app)
manager.add_option('-c', '--config', dest='config', required=False)
if __name__ == '__main__':
manager.run()

Categories

Resources