Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

404 Response when running FlaskClient test method

Tags:

python

flask

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
like image 205
tyoungjr Avatar asked Sep 27 '16 02:09

tyoungjr


1 Answers

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()
like image 75
davidism Avatar answered Oct 20 '22 23:10

davidism