81

app.py

from flask import Flask, render_template, request,jsonify,json,g
import mysql.connector

app = Flask(__name__)

class TestMySQL():
    @app.before_request
    def before_request():
        try:
            g.db = mysql.connector.connect(user='root', password='root', database='mysql')
        except mysql.connector.errors.Error as err:
           resp = jsonify({'status': 500, 'error': "Error:{}".format(err)})
           resp.status_code = 500
           return resp

    @app.route('/')
    def input_info(self):
        try:     
            cursor = g.db.cursor()
            cursor.execute ('CREATE TABLE IF NOT EXISTS testmysql (id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, name VARCHAR(40) NOT NULL, \
                     email VARCHAR(40) NOT NULL UNIQUE)')
            cursor.close()

test.py

from app import *
class Test(unittest.TestCase):         
    def test_connection1(self):  
        with patch('__main__.mysql.connector.connect') as mock_mysql_connector_connect:
            object = TestMySQL()
            object.before_request()  # Runtime error on calling this

I am importing app into test.py for unit testing. On calling 'before_request' function into test.py, it is throwing a RuntimeError: working outside of application context, the same is happening on calling 'input_info()'

moken
  • 3,227
  • 8
  • 13
  • 23
guri
  • 1,521
  • 2
  • 14
  • 20

4 Answers4

125

Flask has an Application Context, and it seems like you'll need to do something like:

def test_connection(self):
    with app.app_context():
        #test code

You can probably also shove the app.app_context() call into a test setup method as well.

starball
  • 20,030
  • 7
  • 43
  • 238
brenns10
  • 3,109
  • 3
  • 22
  • 24
  • 1
    @brenns10 can you please tell more about this? Why does this happen? – aks Jan 15 '18 at 09:30
  • 1
    @aks The linked page probably has a better explanation than I could give. Basically, the way Flask is designed, (1) code should be able to access a global variable containing the current Flask app object, but (2) multiple applications might exist in a single process. Flask manages this by setting up some variables before you handle a request. But when you're testing, you have to set up the "application context" yourself, so that your code can find the correct global variables. – brenns10 Jan 16 '18 at 21:26
  • 21
    I get this for using `jsonify`. It's really bad that a simple JSON conversion requires the app to run "in context". – milosmns Jan 11 '20 at 19:14
  • @milosmns: jsonify is more than a simple json conversion though. It actually builds a proper http response. You can either use json.dumps() directly if you want a simple json conversion. Otherwise I bet even flask.json.dumps will want the context. – Eddie Parker Feb 11 '22 at 23:37
  • 1
    @Eddie Parker: I guess that's exactly what I didn't like. I was expecting the basic (or better: default, well known) functionality to run without any additional requirements (like app context)... and then maybe compose on top of that by adding extra functionality (like HTTP) via some opt-in API. Maybe it's also a communication/documentation issue. I don't know what is being used more - plain JSON conversion or JSON+HTTP, so I could be wrong in assuming that most people want just plain conversion. – milosmns Feb 13 '22 at 10:04
  • Yeah; I hear you. When I started with flask I figured 'jsonify' just meant that: 'make this into json'. If they renamed it to something like "make_json_response' it would fit better with what it does. – Eddie Parker Feb 14 '22 at 17:10
  • I got this issue when version chain is updated. Flask==2.1.3 ---> Flask==2.2.2 Flask-SQLAlchemy==2.5.1 ---> Flask-SQLAlchemy==3.0.2 Especially the `Flask-SQLAlchemy` version upgrade brings this issue. – Zhong Ri Nov 01 '22 at 14:49
14
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///todo.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)

app.app_context().push()

Run in terminal
    >python
    >>>from app import app
    >>>from app import db
    >>>db.create_all()

Now it should work
sathish
  • 149
  • 1
  • 4
5

I followed the answer from @brenns10 when I ran into a similar problem when using pytest.

I followed the suggestion of putting it into test setup, this works:

import pytest
from src.app import app


@pytest.fixture
def app_context():
    with app.app_context():
        yield


def some_test(app_context):
    # <test code that needs the app context>
William Miller
  • 9,839
  • 3
  • 25
  • 46
Paddy Alton
  • 1,855
  • 1
  • 7
  • 11
0

I am using python3.8 and had to use a small variation to the answers already posted. I included the the below in pytests and didn't have to change anything else in the rest of the test file.

from flask import Flask

@pytest.fixture(autouse=True)
def app_context():
    app = Flask(__name__)
    with app.app_context():
        yield

This can also be used with a context manager as well. The main different to note here is the creation of the Flask app within the test file rather than it being imported from the main application file.

smart.aleck
  • 195
  • 1
  • 7