I am learning how to test functions in Python using Mock. I am trying to write a test for a simple function,
#social.route('/friends', methods=['GET', 'POST'])
def friends():
test_val = session.get('uid')
if test_val is None:
return redirect(url_for('social.index'))
else:
return render_template("/home.html")
However, I am stuck at how to try and mock session.get('uid') value. So far, this has been my attempt,
#patch('session', return_value='')
#patch('flask.templating._render', return_value='')
def test_mocked_render(self, mocked, mock_session):
print "mocked", repr(self.app.get('/social/friends').data)
print "was _render called?", mocked.called
This attempt may be completely wrong and this is definitely the wrong way as I am still not able to mock session. However, can someone please guide me in the right way through this? Thanks.
Starting with Flask 0.8 we provide a so called “session transaction” which simulates the appropriate calls to open a session in the context of the test client and to modify it.
Let's give a simple example: app.py
from flask import Flask, session
app = Flask(__name__)
app.secret_key = 'very secret'
#app.route('/friends', methods=['GET', 'POST'])
def friends():
test_val = session.get('uid')
if test_val is None:
return 'uid not in session'
else:
return 'uid in session'
if __name__ == '__main__':
app.run(debug=True)
The test file: test_app.py
import unittest
from app import app
class TestSession(unittest.TestCase):
def test_mocked_session(self):
with app.test_client() as c:
with c.session_transaction() as sess:
sess['uid'] = 'Bar'
# once this is reached the session was stored
rv = c.get('/friends')
assert rv.data == 'uid in session'
if __name__ == '__main__':
unittest.main()
run the tests via python test_app.py.
Documentation: Accessing and Modifying Sessions
Related
I want to know how am I supposed to test my code and see whether it works properly. I want to make sure that it stores the received data to the database. Can you please tell me how am I supposed to do that? While I was searching the forum I found this post but I did not really understand what is going on. here is the code I want to test.
client = MongoClient(os.environ.get("MONGODB_URI"))
app.db = client.securify
app.secret_key = str(os.environ.get("APP_SECRET"))
#app.route("/", methods=["GET", "POST"])
def home():
if request.method == "POST":
ip_address = request.remote_addr
entry_content = request.form.get("content")
formatted_date = datetime.datetime.today().strftime("%Y-%m-%d/%H:%M")
app.db.entries.insert({"content": entry_content, "date": formatted_date, "IP": ip_address})
return render_template("home.html")
and here is the mock test I wrote:
import os
from unittest import TestCase
from app import app
class AppTest(TestCase):
# executed prior to each test
def setUp(self):
# you can change your application configuration
app.config['TESTING'] = True
# you can recover a "test cient" of your defined application
self.app = app.test_client()
# then in your test method you can use self.app.[get, post, etc.] to make the request
def test_home(self):
url_path = '/'
response = self.app.get(url_path)
self.assertEqual(response.status_code, 200)
def test_post(self):
url_path = '/'
response = self.app.post(url_path,data={"content": "this is a test"})
self.assertEqual(response.status_code, 200)
The test_post gets stuck and after some seconds gives an error when reaches app.db.entries.insert({"content": entry_content, "date": formatted_date, "IP": ip_address}) part. Please tell me also how can I retrieve the saved data in order to make sure it is saved in the expected way
This is what I do using NodeJS, not tested at all in python but the idea is the same.
First of all, find a in-memory DB, there are options like pymongo-inmemory or mongomock
Then in your code you have to do the connection according to you environment (production/development/whatever)
Something like this:
env = os.environ.get("ENV")
if env == "TESTING":
# connect to mock db
elif env == "DEVELOMPENT":
# for example if you want to test against a real DB but not the production one
# then do the connection here
else:
# connect to production DB
I don't know if it is the proper way to do it but I found a solution. After creating a test client self.app = app.test_client() the db gets set to localhost:27017 so I changed it manually as follows and it worked:
self.app = app.test_client()
client = MongoClient(os.environ.get("MONGODB_URI"))
I've been struggling with this for awhile now. I Have a flask app that is executed in my app.py file. In this file I have a bunch of endpoints that call different functions from other files. In another file, extensions.py, I've instantiated a class that contains a redis connection. See the file structure below.
#app.py
from flask import Flask
from extensions import redis_obj
app = Flask(__name__)
#app.route('/flush-cache', methods=['POST'])
def flush_redis():
result = redis_obj.flush_redis_cache()
return result
# extensions.py
from redis_class import CloudRedis
redis_obj = CloudRedis()
# redis_class
import redis
class CloudRedis:
def __init__(self):
self.conn = redis.Redis(connection_pool=redis.ConnectionPool.from_url('REDIS_URL',
ssl_cert_reqs=None))
def flush_redis_cache(self):
try:
self.conn.flushdb()
return 'OK'
except:
return 'redis flush failed'
I've been attempting to use monkeypatching in a test patch flush_redis_cache, so when I run flush_redis() the call to redis_obj.flush_redis_cache() will just return "Ok", since I've already tested the CloudRedis class in other pytests. However, no matter what I've tried I haven't been able to successfully patch this. This is what I have below.
from extensions import redis_obj
from app import app
#pytest.fixture()
def client():
yield app.test_client()
def test_flush_redis_when_redis_flushed(client, monkeypatch):
# setup
def get_mock_flush_redis_cache():
return 'OK'
monkeypatch.setattr(cloud_reids, 'flush_redis_cache', get_mock_flush_redis_cache)
cloud_redis.flush_redis = get_mock_flush_redis_cache
# act
res = client.post('/flush-cache')
result = flush_redis()
Does anyone have any ideas on how this can be done?
I am writing a Flask unit test for a function that would return a render template. I tried few ways but it seems not working.
Here is the function:
#app.route('/', methods=['POST'])
#lti(request='initial', error=error, app=app)
def chooser(lti=lti):
return_url = request.form.get('launch_presentation_return_url', '#')
return render_template(
'chooser.html'
)
Few ways that I have been trying:
# 1st way
rv = self.app.post('/')
self.assertTrue('Choose an Icon to Insert' in rv.get_data(as_text=True))
# Error
self.assertTrue('Choose an Icon to Insert' in rv.get_data(as_text=True))
AssertionError: False is not true
# 2nd way
rv = self.app.post('/chooser.html')
assert '<h1>Choose an Icon to Insert</h1>' in rv.data
# Error
assert 'Choose an Icon to Insert' in rv.data
AssertionError
chooser.html
<body>
<h1>Choose an Icon to Insert</h1>
</body>
Thanks for all your helps.
Here an example which can help you to understand. Our application - app.py:
import httplib
import json
from flask import Flask, request, Response
app = Flask(__name__)
#app.route('/', methods=['POST'])
def main():
url = request.form.get('return_url')
# just example. will return value of sent return_url
return Response(
response=json.dumps({'return_url': url}),
status=httplib.OK,
mimetype='application/json'
)
Our tests - test_api.py:
import json
import unittest
from app import app
# set our application to testing mode
app.testing = True
class TestApi(unittest.TestCase):
def test_main(self):
with app.test_client() as client:
# send data as POST form to endpoint
sent = {'return_url': 'my_test_url'}
result = client.post(
'/',
data=sent
)
# check result from server with expected data
self.assertEqual(
result.data,
json.dumps(sent)
)
How to run:
python -m unittest discover -p path_to_test_api.py
Result:
----------------------------------------------------------------------
Ran 1 test in 0.009s
OK
Hope it helps.
I'm using Pytest fixtures with Flask. My application is instantiated using an application factory.
#conftest.py
#pytest.fixture(scope='session')
def app(request):
'''Session-wide test application'''
app = create_app('testing')
app.client = app.test_client()
app_context = app.app_context()
app_context.push()
def teardown():
app_context.pop()
request.addfinalizer(teardown)
return app
I wanted to verify that the app created by my fixture uses Flask's built-in test_client, so I wrote a test:
#test_basics.py
def test_app_client_is_testing(app):
assert app.client() == app.test_client()
When I run this test, I get: TypeError: 'FlaskClient' object is not callable
What am I doing wrong?
Is the test incorrect, or is the fixture incorrect?
app.client is already an instance, you shouldn't call it again. Ultimately, this test makes no sense. Of course client is a test client, that's how you just created it in the fixture. Also, the clients will never be equal, they are different instances.
from flask.testing import FlaskClient
assert app.client == app.test_client() # different instances, never true
assert isinstance(app.client, app.test_client_class or FlaskClient) # still pointless, but correct
What you probably want is two fixtures: app and client, rather than creating a client on the app.
#pytest.yield_fixture
def app():
a = create_app('testing')
a.testing = True
with a.app_context():
yield a
#pytest.yield_fixture
def client(app):
with app.test_client() as c:
yield c
from flask.testing import FlaskClient
def test_app_client_is_client(app, client):
# why?
assert isinstance(client, app.test_client_class or FlaskClient)
I'm using Flask to expose some data-crunching code as a web service.
I'd like to have some class variables that my Flask functions can access.
Let me walk you through where I'm stuck:
from flask import Flask
app = Flask(__name__)
class MyServer:
def __init__(self):
globalData = json.load(filename)
#app.route('/getSomeData')
def getSomeData():
return random.choice(globalData) #select some random data to return
if __name__ == "__main__":
app.run(host='0.0.0.0')
When I run getSomeData() outside of Flask, it works fine. But, when I run this with Flask, I get 500 internal server error. There's no magic here, and Flask has no idea that it's supposed to initialize a MyServer object. How can I feed an instance of MyServer to the app.run() command?
I could admit defeat and put globalData into a database instead. But, is there an other way?
You can create an instance of MyServer just outside the scope of your endpoints and access its attributes. This worked for me:
class MyServer:
def __init__(self):
self.globalData = "hello"
from flask import Flask
app = Flask(__name__)
my_server = MyServer()
#app.route("/getSomeData")
def getSomeData():
return my_server.globalData
if __name__ == "__main__":
app.run(host="0.0.0.0")
I know this is a late reply, but I came across this question while facing a similar issue. I found flask-classful really good.
You inherit your class from FlaskView and register the Flask app with your MyServer class
http://flask-classful.teracy.org/#
In this case, with flask-classful, your code would look like this:
from flask import Flask
from flask_classful import FlaskView, route
app = Flask(__name__)
class MyServer(FlaskView):
def __init__(self):
globalData = json.load(filename)
#route('/getSomeData')
def getSomeData():
return random.choice(globalData) #select some random data to return
MyServer.register(app, base_route="/")
if __name__ == "__main__":
app.run(host='0.0.0.0')
The least-coupled solution is to apply the routes at runtime (instead of at load time):
def init_app(flask_app, database_interface, filesystem_interface):
server = MyServer(database_interface, filesystem_interface)
flask_app.route('get_data', methods=['GET'])(server.get_data)
This is very testable--just invoke init_app() in your test code with the mocked/faked dependencies (database_interface and filesystem_interface) and a flask app that has been configured for testing (app.config["TESTING"]=True or something like that) and you're all-set to write tests that cover your entire application (including the flask routing).
The only downside is this isn't very "Flasky" (or so I've been told); the Flask idiom is to use #app.route(), which is applied at load time and is necessarily tightly coupled because dependencies are hard-coded into the implementation instead of injected into some constructor or factory method (and thus complicated to test).
The following code is a simple solution for OOP with Flask:
from flask import Flask, request
class Server:
def __init__(self, name):
self.app = Flask(name)
#self.app.route('/')
def __index():
return self.index()
#self.app.route('/hello')
def __hello():
return self.hello()
#self.app.route('/user_agent')
def __user_agent():
return self.user_agent()
#self.app.route('/factorial/<n>', methods=['GET'])
def __factorial(n):
return self.factorial(n)
def index(self):
return 'Index Page'
def hello(self):
return 'Hello, World'
def user_agent(self):
return request.headers.get('User-Agent')
def factorial(self, n):
n = int(n)
fact = 1
for num in range(2, n + 1):
fact = fact * num
return str(fact)
def run(self, host, port):
self.app.run(host=host, port=port)
def main():
server = Server(__name__)
server.run(host='0.0.0.0', port=5000)
if __name__ == '__main__':
main()
To test the code, browse the following urls:
http://localhost:5000/
http://localhost:5000/hello
http://localhost:5000/user_agent
http://localhost:5000/factorial/10
a bit late but heres a quick implementation that i use to register routes at init time
from flask import Flask,request,render_template
from functools import partial
registered_routes = {}
def register_route(route=None):
#simple decorator for class based views
def inner(fn):
registered_routes[route] = fn
return fn
return inner
class MyServer(Flask):
def __init__(self,*args,**kwargs):
if not args:
kwargs.setdefault('import_name',__name__)
Flask.__init__(self,*args ,**kwargs)
# register the routes from the decorator
for route,fn in registered_routes.items():
partial_fn = partial(fn,self)
partial_fn.__name__ = fn.__name__
self.route(route)(partial_fn)
#register_route("/")
def index(self):
return render_template("my_template.html")
if __name__ == "__main__":
MyServer(template_folder=os.path.dirname(__file__)).run(debug=True)
if you wish to approach MyServer class as a resource
I believe that flask_restful can help you:
from flask import Flask
from flask_restful import Resource, Api
import json
import numpy as np
app = Flask(__name__)
api = Api(app)
class MyServer(Resource):
def __init__(self):
self.globalData = json.load(filename)
def get(self):
return np.random.choice(self.globalData)
api.add_resource(MyServer, '/')
if __name__ == '__main__':
app.run()