Writing unit test cases for flask application [duplicate] - python

This question already has an answer here:
Testing code that requires a Flask app or request context
(1 answer)
Closed 4 years ago.
I'm making a web application using flask framework. To display a web page using flask, I use a render_template() function.
For example:
#app.route('/restaurants/<int:restaurant_id>/')
def restaurantMenu(restaurant_id):
try:
# pdb.set_trace()
session = get_session()
restaurant = session.query(Restaurant).filter_by(id=restaurant_id).one()
if restaurant is not None:
menu_items = session.query(MenuItem).filter(
MenuItem.restaurant_id == restaurant.id
)
session.close()
if menu_items is not None:
return render_template('menu.html', restaurant=restaurant, items=menu_items)
except NoResultFound:
return render_template('error.html', error_message='No such restaurant id.')
And
#app.route('/')
def welcomePage():
return render_template('index.html')
How do I write test cases for functions like these? I'm new to testing so I wanted to write test cases for my code.

You can create a test app in your tests and call it very simply, like so:
app = create_app().test_client()
result = app.get('/')
assert('something' in result.data)
That gives you the general idea - it's pretty easy to work with. More information can be found in the Flask Testing Docs: http://flask.pocoo.org/docs/0.10/testing/

Related

Flask app, global variable over multiple files [duplicate]

This question already has answers here:
Are global variables thread-safe in Flask? How do I share data between requests?
(4 answers)
What is the g object in this Flask code?
(1 answer)
Closed 1 year ago.
I'm trying to share a variable I defined in my main function with a flask app which is imported from another file. First I tried to solve it via a classic global variable which did not bring me any further, until I stumbled over the concept of flask.g and the app context.
So I tried the following: I have two files in the same directory, a main.py:
# main.py
import app
from flask import g
if __name__ == "__main__":
with app.app.app_context():
g.value = "Hello World"
app.app.run(debug=True, threaded=True, host='localhost', port=5000)
and a app.py:
# app.py
from flask import Flask, g
app = Flask(__name__)
#app.route('/helloworld')
def send_response():
return g.value
However, when I request at http://localhost:5000/helloworld I get
AttributeError: '_AppCtxGlobals' object has no attribute 'value'
So it seems that setting the value g.value in one file is not reflected in the app.
I'm a beginner in Flask and it is very likely I did not get the concept right.
Similar questions did not get me any answer I could use to fix the issue:
Flask passing global variable, python-How to set global variables in Flask?, Preserving global state in a flask application
Help would be much appreciated!
Just use a regular Python module-level variable.
# app.py
g = "Example"
#app.route("/example")
def example_endpoint():
return g
# main.py
import app
app.g = "Hello"
Quoting the same page you linked:
The application context is created and destroyed as necessary. When a Flask application begins handling a request, it pushes an application context and a request context. When the request ends it pops the request context then the application context. Typically, an application context will have the same lifetime as a request.
So your setting flask.g outside of a request context (in your main.py) doesn't carry your value to anywhere.

How to pytest a Flask Endpoint

I'm getting started with Flask and Pytest in order to implemente a rest service with unit test, but i'm having some troouble.
I'll like to make a simple test for my simple endpoint but i keep getting a Working outside of application context. error when running the test.
This is the end point:
from flask import jsonify, request, Blueprint
STATUS_API = Blueprint('status_api', __name__)
def get_blueprint():
"""Return the blueprint for the main app module"""
return STATUS_API
#STATUS_API.route('/status', methods=['GET'])
def get_status():
return jsonify({
'status' : 'alive'
})
And this is how I'm trying to test it (i know it should fail the test):
import pytest
from routes import status_api
def test_get_status():
assert status_api.get_status() == ''
I'm guessing I just cant try the method with out building the whole app. But if that's the case i dont really know how to aproach this problem
The Flask documentation on testing is pretty good.
Instead of importing the view functions, you should create a so called test client, e.g. as a pytest fixture.
For my last Flask app this looked like:
#pytest.fixture
def client():
app = create_app()
app.config['TESTING'] = True
with app.app_context():
with app.test_client() as client:
yield client
(create_app is my app factory)
Then you can easily create tests as follows:
def test_status(client):
rv = client.get('/stats')
assert ...
As mentioned at the beginning, the official documentation is really good.
Have you considered trying an API client/development tool? Insomnia and Postman are popular ones. Using one may be able to resolve this for you.

Can Flask server cache request body? [duplicate]

This question already has answers here:
Are global variables thread-safe in Flask? How do I share data between requests?
(4 answers)
Closed 3 years ago.
I have a small flask API setup as following,
from flask import Flask, request, jsonify, Response
import json
import subprocess
import os
app = Flask(__name__)
shellScripts = {
'redeploy': ['/bin/bash', 'redeploy.sh'],
'script-exec': ['/bin/bash', 'script-exec.sh']
}
def prepareShellCommand(json_data, scriptKey):
script=shellScripts[scriptKey]
print('script is')
print(script)
for key in json_data:
if scriptKey == 'redeploy':
script.append("-{0}".format(key[0]))
script.append(json_data[key])
return script
#app.route('/redeploy', methods=['POST'])
def setup_redeploy():
branches_data_json = request.get_json()
if ('frontendBranch' not in branches_data_json and 'backendBranch' not in branches_data_json):
return jsonify({'error': 'Need to provide at least one branch'}), 400
command = prepareShellCommand(branches_data_json, 'redeploy')
sp = subprocess.Popen(command)
return jsonify({'message': 'Redeployment under process'}), 201
#app.route('/execute', methods=['POST'])
def execute_script():
script_data_json = request.get_json()
if ('scriptPath' not in script_data_json):
return jsonify({'error': 'Need to provide script path'}), 400
command = prepareShellCommand(script_data_json, 'script-exec')
sp = subprocess.Popen(command)
return jsonify({'message': 'Script execution under process'}), 201
What's happening is, say I initiate an API endpoint, /execute with some data as {scriptPath: 'some-file'}, and it runs successfully. However, sometimes, regardless of change in the request body data, the API seems to work with the old data, {scriptPath: 'some-file'}, even if I am initiating the API with something like {scriptPath: 'new-file'}. And it doesn't change until I kill the python process, and restart it.
What could be the reason for this? I am running this as a development server, on a google cloud instance.
It's happening with both the endpoints, and I have a gut feeling that it's got something to do with either the subprocess or the dictionary that contains the boilerplate.
Can anyone help me with this?
This is almost certainly because you have defined shellScripts at module level but modify it from your handlers. The changes to the values of that dictionary will persist for the lifetime of the server process.
You should copy the value and modify that instead:
def prepareShellCommand(json_data, scriptKey):
script = shellScripts[scriptKey].copy()

Flask Counter for a Game [duplicate]

This question already has answers here:
Are global variables thread-safe in Flask? How do I share data between requests?
(4 answers)
Closed 4 years ago.
I'm creating a game on Flask and want to have a counter across the entire session. I put together some psuedo code for the general outline of the project. Basically it's a script that pulls a random question from a database, collects user input, returns the correct answer, and then starts over again at the user input route. I want to add a counter that will appears regardless of the function of route. I looked into Flask sessions, but it was confusing where or how to implement... any thoughts/ suggestions? Sessions or otherwise?
get_db_info():
connects to a database
#app.route('/questions')
def user_input():
collects user input and puts into variable
#app.route('/answers')
def results():
if user input = results:
print(correct!)
elif:
print(incorrect)
renders back to user_input()
You already mention sessions, I think they are the solution to your problem:
Flask-Session is an extension for Flask that adds support for
Server-side Session to your application.
Here is an example of doing this with a session. It stores three counters, one for total accesses to the app and two for accesses to individual routes. If you call /A three times and then /B, it will give you
accessed B 1 times, accessed app 4 times
from flask import Flask, session
from flask_session import Session
app = Flask(__name__)
# Check Configuration section for more details
SESSION_TYPE = 'filesystem'
app.config.from_object(__name__)
Session(app)
#app.route('/')
def reset():
session["counterA"]=0
session["counterB"]=0
session["counterTotal"]=0
return "all counters were reset"
#app.route('/A')
def routeA():
if not "counterA" in session:
session["counterA"]=0
if not "counterTotal" in session:
session["counterTotal"]=0
session["counterA"]+=1
session["counterTotal"]+=1
return "accessed A {} times, accessed app {} times".format(session["counterA"], session["counterTotal"])
#app.route('/B')
def routeB():
if not "counterB" in session:
session["counterB"] = 0
if not "counterTotal" in session:
session["counterTotal"] = 0
session["counterB"] += 1
session["counterTotal"] += 1
return "accessed B {} times, accessed app {} times".format(session["counterB"], session["counterTotal"])
if __name__ == '__main__':
app.run()
The session behaves like a dictionary and is available across all routes. You can simply put your counter there.
For the sake of completeness, a solution without sessions is also possible:
In principle, the flask routes behave just like python functions.
So the following will work:
counter = 0
#app.route('/questions')
def user_input():
global counter
print(counter)
counter+=1
#app.route('/answers')
def results():
global counter
print(counter)
counter+=1
I would discourage the use of globals, they can make your code very hard to read and are not thread safe.

how to save output from the shell by using python flask [duplicate]

This question already has an answer here:
Testing code that requires a Flask app or request context
(1 answer)
Closed 5 years ago.
First of all, I am very new at programming.
I am trying to save a variable from bash shell
>curl http://169.254.169.254/latest/meta-data/
this line would return data such as local-ipv4. And I am trying to use phython and flask to save those variables. I wrote
from flask import Flask, request
app = Flask(__name__)
#app.route('/')
def testRequest():
url1 = "http://169.254.169.254/latest/meta-data/"
name1 = request.get(url1)
nameText = name1.text
return nameText
testOutput = testRequest()
print testOutput
But this gives me runtime error : working outside of the request context.
can someone guide me to where to look for possible solution?
To things to comment here:
First, it seems that you'll be just fine by using requests, a highly recommended library for HTTP requests. With it you could do:
import requests
url = "http://169.254.169.254/latest/meta-data/"
resp = requests.get(url)
print resp.text
Regards to the error you're getting runtime error : working outside of the request context, is because by testOutput = testRequest() you're calling a method that's part of the Flask app app. Another thing related to the error is that you never ran the Flask app. To do this, include this at the end of your code.
if __name__ == '__main__':
app.run()
But again, Flask is rather a web framework that it's useful to create web sites, APIs, web apps, etc. It's very useful, but I don't think you may need it for what you're trying to achieve.
Further info about requests and Flask:
http://docs.python-requests.org/
http://flask.pocoo.org/
Since you only need to make an HTTP GET request and print the response, you don't need Flask. You can use the urllib standard library to send the GET request (https://docs.python.org/3/library/urllib.request.html):
import urllib.request
def testRequest():
url1 = "http://169.254.169.254/latest/meta-data/"
response = urllib.request.urlopen(url1)
nameText = response.read().decode('utf-8')
return nameText
testOutput = testRequest()
print testOutput

Categories

Resources