How to mock a function in a flask get request? - python

I recently wrote a webapi and i need to test a function. But im having trouble returning the mock value. Heres my hello.py:
from flask import Flask, request
import traceback
import json
app = Flask(__name__)
#app.route("/main")
def hello_world():
return "<p>Hello, World!</p>"
def determine_env(check):
if check == 'test':
return 'test'
elif check == 'hello_prod':
return 'prod'
elif check == 'hello_dev':
return 'dev'
else:
return "you must have gotten the wrong env"
#app.route("/main/rate_and_borrow_data")
def rate_and_borrow_data():
try:
print("testing request method")
print(request.args)
directory = request.args['udl']
env = determine_env(directory)
print(env)
args = request.args.getlist("args")
print(f"The arguments are: {args}")
if request.args.get("data") == 'rate':
print("the rate is in there")
return "finished"
except:
traceback.print_exc()
return json.dumps({'error': traceback.format_exc()})
if __name__ == "__main__":
app.run(host="0.0.0.0", port=81, debug=True)
Now i want to write unittests on this api. my directory structure is as follows:
Test-flask-connectivity > app > hello.py
> tests > test_hello.py
Inside test_hello.py, i have written the following tests:
import unittest
import requests
import os
from unittest.mock import patch
from app.hello import determine_env
class TestFlaskApi(unittest.TestCase):
def setUp(self):
self.payload = {'data': 'rate', 'file_path': 'nwklgat/test', 'args': ['AAPL', '3', 'TMI'], 'url': 'http://localhost:81/main/rate_and_borrow_data',
'udl': 'test'}
#patch('app.hello.determine_env', return_value='monkeys_love_development')
def test_rate_and_borrow_data(self, m1):
url = self.payload['url']
r = requests.get(url, params=self.payload)
if __name__ == '__main__':
unittest.main()
Now, im expecting the print statement to say "monkeys love development" for the statement
env = determine_env(directory)
since i patched the above function. But its not giving me this value. Its giving me "test". May i know what i did wrong? Thanks

Related

Pytest patch function used by global variable

I need to test a code that has a global variable which is populated by a function that returns a value after making a connection to external server. For the pytest, I need to figure out a way so that the function does not get called.
I've tried patching both the global variable and the function, but neither worked.
Here's the python code - my_module.py:
import datetime
# %%
def server_function_1 ():
try:
return_val = "Assume the external server function returns a string"
except Exception as e:
print("Failed")
print(e)
raise e
else:
return return_val
finally:
# assume when called from the testing environment, the server connection will raise a exception
raise Exception("Cannot connect to server")
global_result_of_server_func = server_function_1()
# %%
def get_current_datetime_str():
return datetime.datetime.now().strftime('%Y%m%d.%H%M%S.%f')
# %%
def some_function():
return global_result_of_server_func, get_current_datetime_str()
Here's the pytest file - test_my_module.py:
# %%
import pytest
from unittest import mock
import datetime
import logging
import sys
# %%
import my_module
logger = logging.getLogger(__name__)
logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)
frozen_time = datetime.datetime(year=2022, month=6, day=1, hour=1, minute=0, second=0, microsecond=0)
mock_server_str = "Mock Server String"
# %%
class TestMyModule () :
def test_handler_1 (self):
with mock.patch("my_module.get_current_datetime_str", return_value=frozen_time.strftime('%Y%m%d.%H%M%S.%f')), \
mock.patch("my_module.global_result_of_server_func", new=mock_server_str):
test_server_val, test_frozen_time = my_module.some_function()
assert test_frozen_time == frozen_time.strftime('%Y%m%d.%H%M%S.%f')
assert test_server_val == mock_server_str
def test_handler_2 (self):
with mock.patch("my_module.get_current_datetime_str", return_value=frozen_time.strftime('%Y%m%d.%H%M%S.%f')), \
mock.patch("my_module.server_function_1", return_value=mock_server_str):
test_server_val, test_frozen_time = my_module.some_function()
assert test_frozen_time == frozen_time.strftime('%Y%m%d.%H%M%S.%f')
assert test_server_val == mock_server_str
What I am trying to achieve is that variable global_result_of_server_func gets the mock value, and the function server_function_1 doesn't get called and tries to make a connection to the server.
Thanks.
Delaying the import like the suggestion in this question didn't seem to make any difference.

Python Flask infinite loop use global list which can be extended

I would like to run an infinite loop in flask, which do something with a global list.
I'd like to append the list through an API call, and process data from the updated list.
What is the problem?
Usage: you run flask application, and call localhost:5000/ to append the list.
It will return the new list, but in the loop, it remains the initial list.
Thanks
import time
from flask import Flask
from multiprocessing import Process, Value
app = Flask(__name__)
stuff = [1, 2]
#app.route('/', methods=['GET'])
def index():
global stuff
stuff.append(max(stuff) + 1)
print('in request, stuff: ', stuff)
return ', '.join(map(str, stuff))
def print_stuff():
global stuff
print('in loop, stuff: ', stuff)
def record_loop(loop_on):
while True:
if loop_on.value == True:
print_stuff()
time.sleep(1)
if __name__ == "__main__":
recording_on = Value('b', True)
p = Process(target=record_loop, args=(recording_on,))
p.start()
app.run(debug=True, use_reloader=False)
p.join()
I found the working solution:
import time
from flask import Flask
from flask_apscheduler import APScheduler
app = Flask(__name__)
scheduler = APScheduler()
i = 0
def scheduleTask():
global i
print("This test runs every 1 seconds", i)
time.sleep(2)
#app.route('/')
def hello():
global i
i += 1
return str(i)
if __name__ == '__main__':
scheduler.add_job(id = 'Scheduled Task', func=scheduleTask, trigger="interval", seconds=1)
scheduler.start()
app.run(host="0.0.0.0")

Unit Tests for FLASK API

I have a basic flask API running :
u/app.route('/helloworld', methods = ['GET'])
def first_api():
hname = "hello"
lhname = "world"
print(hname+lhanme)
Now I need to add some unit tests to it, and here is my unit test file:
import json
def test_index(app, client):
res = client.get('/helloworld')
assert res.status_code == 200
assert "hello" in res.data
How can I pass value for variables hname and lhname from this unit test?
Here is my conf file for pytest:
import pytest
from app import app as flask_app
u/pytest.fixture
def app():
return flask_app
u/pytest.fixture
def client(app):
return app.test_client()
You have a little mistake in your endpoint. You want it to return the string instead of printing it. Please consider the following example:
from flask import Flask, request
flask_app = Flask(__name__)
app = flask_app
#app.route('/helloworld', methods = ['GET'])
def first_api():
hname = request.args.get("hname")
lhname = request.args.get("lname")
print(hname)
return hname + lhname
def test_index(app):
client = app.test_client()
hname = "hello"
lname = "world"
res = client.get('/helloworld?hname={}&lname={}'.format(hname, lname))
assert res.status_code == 200
assert "hello" in res.data.decode("UTF-8")
if __name__ == "__main__":
test_index(app)

Is there a way to render multiple templates and run a function at the same time in Flask?

I want to know how to return render_template('start-crawl.html') and have a function run at the same time. Once the function completes I want to return render_template('finish-crawl.html').
from flask import Flask, render_template, request, make_response, redirect, url_for
from flask_executor import Executor
app = Flask(__name__)
executor = Executor(app)
has_ran = False
#app.route('/', methods=["POST", "GET"])
def index():
if request.method == "POST":
usr_input = form["usr_input"]
def func():
"""
some code
global has_ran
has_ran = True
"""
executor.submit(func)
try:
return render_template('start-crawl.html')
finally:
x = 5 # Use this variable for loop but other than that it is useless
while x == 5:
if has_ran:
return render_template('finish.html')
else:
return render_template('index.html')
if __name__ == '__main__':
app.run(debug=True)
How I want this code to function is when func() is called through executor.submit(func), it starts running it and while it's running return render_template('start-crawl.html'). After executor.submit(func) finishes, it return render_template('finish.html'). Currently what happens is when I press submit on the form, the page keeps loading and then return render_template('finish.html') without return render_template('start.html'). What is the problem here?

Input from another file

I've got 2 python files. This is the first one:
class Downloader():
def __init__(self):
baseURL = 'https://example.com'
def getDownloadLink(self):
#linkBase = input("Text: ")
responseBase = requests.get(linkBase).content
soupBase = BeautifulSoup(responseBase, 'lxml')
And second python file:
from flask import Flask
from flask import request
from flask import render_template
from firstFile import Downloader
app = Flask(__name__)
#app.route('/')
def my_form():
return render_template("form.html")
#app.route('/', methods=['POST'])
def my_form_post():
linkBase = request.form['text']
#processed_text = text.upper()
return Downloader().getDownloadLink()
if __name__ == '__main__':
app.run()
It gives me error:
NameError: name 'linkBase' is not defined
Is it possible to connect linkBase from first file with linkBase in second file ?
The problem here is that you're trying to access a variable that doesn't exist in the scope of your getDownloadLink function.
One solution would be to add linkBase as an argument:
def getDownloadLink(self, linkBase):
responseBase = requests.get(linkBase).content
soupBase = BeautifulSoup(responseBase, 'lxml')
And then modify your route to send the value to the function:
#app.route('/', methods=['POST'])
def my_form_post():
linkBase = request.form['text']
return Downloader().getDownloadLink(linkBase)
Modify your code to pass the value as an argument:
class Downloader():
def __init__(self):
baseURL = 'https://example.com'
def getDownloadLink(self, linkBase):
#linkBase = input("Text: ")
responseBase = requests.get(linkBase).content
soupBase = BeautifulSoup(responseBase, 'lxml')
Second file:
from flask import Flask
from flask import request
from flask import render_template
from firstFile import Downloader
app = Flask(__name__)
#app.route('/')
def my_form():
return render_template("form.html")
#app.route('/', methods=['POST'])
def my_form_post():
linkBase = request.form['text']
#processed_text = text.upper()
return Downloader().getDownloadLink(linkBase)
if __name__ == '__main__':
app.run()

Categories

Resources