I'm building CRUD REST APIs using peewee ORM and sanic(sanic-crud) as app server. Things are working fine. And I wrote couple of unittest cases for the same.
But, I'm facing problem running unittests. The problem is that unittests starts sanic app server and stalled there. Its not running unittest cases at all. But when I press Ctrl+C manually then the sanic server gets terminated and unittests execution starts. So, it means there should be a way to start sanic server and continue unittests run and terminate server at the end.
Can someone please me the correct way writting unittest cases for sanic app?
I followed official docs too but no luck.
http://sanic.readthedocs.io/en/latest/sanic/testing.html
I tried following
from restapi import app # the execution stalled here i guess
import unittest
import asyncio
import aiohttp
class AutoRestTests(unittest.TestCase):
''' Unit testcases for REST APIs '''
def setUp(self):
self.loop = asyncio.new_event_loop()
asyncio.set_event_loop(None)
def test_get_metrics_all(self):
#asyncio.coroutine
def get_all():
res = app.test_client.get('/metrics')
assert res.status == 201
self.loop.run_until_complete(get_all())
from restapi.py
app = Sanic(__name__)
generate_crud(app, [Metrics, ...])
app.run(host='0.0.0.0', port=1337, workers=4, debug=True)
Finally managed to run unittests by moving app.run statement to main block
# tiny app server starts here
app = Sanic(__name__)
generate_crud(app, [Metrics, ...])
if __name__ == '__main__':
app.run(host='0.0.0.0', port=1337, debug=True)
# workers=4, log_config=LOGGING)
and
from restapi import app
import json
import unittest
class AutoRestTests(unittest.TestCase):
''' Unit testcases for REST APIs '''
def test_get_metrics_all(self):
request, response = app.test_client.get('/metrics')
self.assertEqual(response.status, 200)
data = json.loads(response.text)
self.assertEqual(data['metric_name'], 'vCPU')
if __name__ == '__main__':
unittest.main()
Related
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.
I am running unit test to test an API that works fine when tested with Postman. The API takes in two parameters in the form {"body":"hey","title":"title"} adds these values to the database based on the models I have made. A response is returned in similar format with an extra key of id which is obtained from the database. The thing is that it works fine with Postman. However, when tested using the Pytest, just does not work.
Here is the code in the test file.
import os
import unittest
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
# from flask_backend import app
# from flask_backend.core import db
class BasicTests(unittest.TestCase):
app = Flask(__name__)
db = SQLAlchemy(app)
def setUp(self):
file_path = os.path.abspath(os.getcwd()) + "\database_test.db"
self.app.config['TESTING'] = True
self.app.config['DEBUG'] = False
self.app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + file_path
self.app = self.app.test_client()
self.db.drop_all()
self.db.create_all()
def tearDown(self):
self.db.session.remove()
class TestApi(BasicTests):
def test_add_post(self):
url = 'http://127.0.0.1:5000'
parameters = {'body': 'Body', 'title': 'title'}
response = self.app.post(url+'/api/dbapi/post/', data=parameters)
print(self.app)
self.assertEqual(response.status_code, 200)
if __name__ == '__main__':
unittest.main()
I am thinking that while executing the test a server is not started and hence the 404 error is raised.
The reason I am not importing the app variable from the project itself is because the module is not getting imported. I have asked the question in a different thread. Here is the link to it:
Can not import the files from parent directory even though it has __init__.py file in it
From what I understand, if I can import the app instance that is used in the project itself, I should be good but that isn't working either.
Here is how I solved the problem. So, apparently it was required to get the server running for the API to be accessible and the test to run successfully.
So, I added app.run() at the end of the code and it worked fine. Here is what it looks like
if __name__ == '__main__':
unittest.main()
app.run()
for reasons I want to trigger the reboot of an raspberry pi using a REST api.
My REST api is coded in python flask like this:
from flask import Flask
from flask import jsonify
import subprocess
app = Flask(__name__)
#app.route('/api/reboot')
def reboot():
subprocess.call("/sbin/reboot")
return jsonify(triggered='reboot')
if __name__ == '__main__':
app.run(debug=True,host="0.0.0.0")
the code is working perfectly fine. But due to its a reboot the return will not be send (because the system is rebooting obviously).
Is there a way to trigger the reboot some kind of async with a delay of a couple milliseconds, that allows to return some value (in my case just an custom 'ack') prior the actual reboot?
Try threading.Timer:
For example:
from flask import Flask
from flask import jsonify
import subprocess
import threading
app = Flask(__name__)
def _reboot():
subprocess.call("/sbin/reboot")
#app.route('/api/reboot')
def reboot():
t = threading.Timer(1, _reboot)
t.start()
return jsonify(triggered='reboot')
if __name__ == '__main__':
app.run(debug=True,host="0.0.0.0")
I am running a flask app using celery to offload long running process on a IIS 6.5 server and using python 2.7
The choice of the python 2.7, flask and IIS 7 server are imposed by the Company and cannot be changed.
The flask app works on IIS server (so the server set-up should be correct), except for the following.
I am struggling to find the good implementation to make flask works smoothly on the server.
I searched for similar questions on the site, but none helped so far.
When I am running the flask app on my pc, the application only performs as expected if I use OPTION A or OPTION B.
OPTION A:
from flask import Flask
app = Flask(__name__)
#app.route("/")
def hello():
return "Hello from FastCGI via IIS!"
if __name__ == "__main__":
app.run(threaded=True) # <--- This works
OPTION B:
If I wrap the flask app inside tornado, it works well as well:
from tornado.wsgi import WSGIContainer
from tornado.httpserver import HTTPServer
from tornado.ioloop import IOLoop
from myapp import app
http_server = HTTPServer(WSGIContainer(app))
http_server.listen(5000)
IOLoop.instance().start()
OPTION C:
However if I run the flask app only with the default parameters, then it does not work the webserver is not returning the view that should be returned after a task has been offloaded to celery.
from flask import Flask
app = Flask(__name__)
#app.route("/")
def hello():
return "Hello from FastCGI via IIS!"
if __name__ == "__main__":
app.run() # <--- Default App
Example of view working for OPTION A & B but not for OPTION C or on ISS:
Here below a stripped down example of a async task that is picked by celery. Note that I'm using Flask-Classy to generate the views, so the code is slightly different than the traditional routing in Flask.
class AnalysisView(FlaskView):
### Index page ---------------
def index(self):
return render_template('analysis.intro.html')
### Calculation process ------
def calculate(self, run_id):
t_id = task_create_id(run_id)
current_analysis = a_celery_function.apply_async(args=[x, y], task_id=t_id)
# Redirect to another view --> Not working when OPTION C or on ISS server
return redirect(url_for('AnalysisView:inprogress', run_id=run_id))
### Waiting page ---------------
def inprogress(self, run_id=run_id):
return render_template('analysis.inprogress.html')
My main problem is how can I use OPTION A (preferably, as it involves less change for me) or OPTION B to work together with IIS server?
Is there a way to tell flask to run with threaded=True during the Flask() initialization or via config settings?
Thanks in advance for you help.
I am not able to successfully use Python Requests to call a second route in the same application using Flask. I know that its best practice to call the function directly, but I need it to call using the URL using requests. For example:
from flask import Flask
import requests
app = Flask(__name__)
#app.route("/")
def hello():
return "Hello World!" # This works
#app.route("/myrequest")
def myrequest():
#r = requests.get('http://www.stackoverflow.com', timeout=5).text # This works, but is external
#r = hello() # This works, but need requests to work
r = requests.get('http://127.0.0.1:5000/', timeout=5).text # This does NOT work - requests.exceptions.Timeout
return r
if __name__ == "__main__":
app.run(debug=True, port=5000)
Your code assumes that your app can handle multiple requests at once: the initial request, plus the request that is generated while the initial is being handled.
If you are running the development server like app.run(), it runs in a single thread by default; therefore, it can only handle one request at a time.
Use app.run(threaded=True) to enable multiple threads in the development server.
As of Flask 1.0 the development server is threaded by default.