from flask import request
from flask.views import MethodView
from unittest.mock import patch
from router.views.time_view import (
_utcnow
)
class StateSetupView(MethodView):
#app.route("/state-setup", methods=['POST'])
def post():
print(f"Non-mocked version: {_utcnow()}")
with patch('router.views.time_view._utcnow', return_value = "LOL"):
print(f"Mocked version: {_utcnow()}")
I can't seem to mock the function return value in runtime. The code above returns the same value in both instances. I do not want to wrap this in a pytest, this needs to work in a view.
Just simple replacing the function straight up worked.
router.views.time_view._utcnow = lambda: 12346789
Related
I am trying to follow instructions from the Django documentation:
https://docs.djangoproject.com/en/3.2/topics/testing/overview/
However when I try to create the object for the test. The object is empty??
This is my code:
from django.test import TestCase, Client
from django.urls import reverse
from .models import ShortURL
from .views import generate_shortURL, redirect, home
from datetime import datetime
url = "https://stackoverflow.com/"
time = datetime.now()
class TestURLShortener(TestCase):
def setup(self):
self.client = Client()
obj= ShortURL.objects.create(original_url=url, short_url='zqSkSQ', time_date_created=time, count=2)
obj.save()
def test_creating_short_URL_POST(self):
"""
Test to create short Urls
"""
short_url_from_db = ShortURL.objects.all()
print(f'short_url_from_db : {short_url_from_db}')
response = self.client.post(reverse('generate_shortURL'), data={'original_url': url})
generated_short_url = response.context["chars"]
self.assertEquals(generated_short_url, 'afasdf')
This is the results when I run the test:
short_url_from_db prints out this <QuerySet []> instead of the object I wanted it to print out from the setup function.
How can I get the object I created to use in this test?
You need to use setUp and not setup as the function for setting up your test case.
You also don't need to call save() if you use create().
An alternative you could make use of is setUpTestData()
This technique allows for faster tests as compared to using setUp().
class TestURLShortener(TestCase):
#classmethod
def setUpTestData(cls):
# Set up data for the whole TestCase
cls.obj = ShortURL.objects.create(original_url=url, short_url='zqSkSQ', time_date_created=time, count=2)
...
def setUp(self):
self.client = Client()
def test_obj_type(self):
self.assertTrue(isinstance(self.obj, ShortURL))
I have a simple app wrote with Flask Appbuilder, the view.py is as follows. It is part of exmaple in http://flask-appbuilder.readthedocs.io/en/latest/views.html with a little change in method1 where I replaced return 'Hello' with a function that I wish to find.
We can change the language in app (en,fr,ru,...) and translate it. Is there a function to get the current language? (Current_Language()).
from flask_appbuilder import AppBuilder, BaseView, expose, has_access, ModelView
from app import appbuilder, db
from flask import render_template, g
from flask_babel import Babel
from flask_babel import lazy_gettext as _
from flask_appbuilder.models.sqla.interface import SQLAInterface
class MyView(BaseView):
default_view = 'method1'
#expose('/method1/')
#has_access
def method1(self):
return Current_Language()
appbuilder.add_view(MyView, "Method1", category='My View')
The appbuilder instance has a bm attribute, which is an instance of the BabelManager class.
This class has a get_locale method that returns the current language your app is using.
class MyView(BaseView):
default_view = 'method1'
#expose('/method1/')
#has_access
def method1(self):
return appbuilder.bm.get_locale()
You can check the code for the BabelManager class on the project repository.
There is an ambiguity in your question. Do you mean the current server-side language or the client-side language.
The former:
import locale
locale.getlocale()
The latter:
from flask import request
request.headers.get('your-header-name')
The header you are interested in is Accept-Language. But there are caveats when it comes to inferring the client language that way. See https://www.w3.org/International/questions/qa-accept-lang-locales
I have a model class defined as;
class SiteConfig(object):
def __init__(self,command,category,frequency,delay):
self.command=command
self.category=category
self.frequency=frequency
self.delay=delay
im calling this model from another class like;
from flask import Flask,request,jsonify
import httplib
from models.SiteConfig import *
app = Flask(__name__)
#app.route('/')
def index():
return "Endpoint is not configured", 500
#app.route('/SitePollingConfiguration',methods=['GET'])
def SitePollingConfiguration():
response = jsonify(statusCode=httplib.OK, config=BuildSiteConfig())
response.status_code = httplib.OK
return response
def BuildSiteConfig():
a= SiteConfig('20c','1',60,60)
print (a)
return a
if __name__ == '__main__':
app.run()
But im getting SiteConfig is not JSON serializable error.
How can i serialize the class object in python?
EDIT
i try to use existing flask json encoder;
modified my siteconfig class like;
from flask import Flask, jsonify
from flask.json import JSONEncoder
class JsonSerializable(object):
def toJson(self):
return JSONEncoder.default(self.__dict__)
class SiteConfig(JsonSerializable):
def __init__(self,command,category,frequency,delay):
self.command=command
self.category=category
self.frequency=frequency
self.delay=delay
And then calling in my main class like;
def ATGSitePollingConfiguration():
response = jsonify(statusCode=httplib.OK, config=BuildATGSiteConfig())
response.status_code = httplib.OK
return response
def BuildATGSiteConfig():
a= SiteConfig('20c','1',60,60)
print (a)
return a
But still serializable issue. Sorry im new to python;
can anyone provide simple code sample on how to achieve this?
By default, flask uses flask.json.JSONEncoder to encode objects as JSON. In order to jsonify your user-defined classes you will need to create a subclass of JSONEncoder that implements the serialisation of your class. How you implement the serialisation is up to you, but the easiest method is probably to convert your object into something that JSONEncoder already knows about (like a dict).
Try adding this code to your app. (You should use the first version of the SiteConfig class that you posted, not your modified one).
from flask.json import JSONEncoder
# A customized JSON encoder that knows about your SiteConfig class
class CustomJSONEncoder(JSONEncoder):
def default(self, obj):
if isinstance(obj, SiteConfig):
return obj.__dict__
return JSONEncoder.default(self, obj)
# Tell your flask app to use your customised JSON encoder
app.json_encoder = CustomJSONEncoder
With this you should get the response:
{
"config": {
"category": "1",
"command": "20c",
"delay": 60,
"frequency": 60
},
"statusCode": 200
}
See here for a similar example.
You may also consider jsonpickle:
import jsonpickle
frozen = jsonpickle.encode(a)
Here is my test_server.py file:
from flask_testing import TestCase
class TestServer(TestCase):
def create_app(self):
import server
app = server.run()
return app
def test_1(self):pass
def test_2(self):pass
server.run does many things and ends by returning the app:
import flask
app = flask.Flask(__name__)
def run():
#many things that don't seem related to the issue
import views
views.run(app=app)
return app
views.run declares many of the the #app.route endpoints. Here is a minimal case:
import flask
def run(app):
#app.route("/", methods=['GET'])
def index():
return flask.render_template('index.html')
If I comment one of the two tests, everything goes fine. If I leave both of them I get:
AssertionError: View function mapping is overwriting an existing endpoint function: index
From what I understand about how this works the problem is caused because views.run is called twice. However I don't see any reason why it is the same instance that is used for both calls.
When tested "by hand" the server works fine, the problem only comes when using unit tests. The code above uses flask_testing but I had the same issue sooner with unittest.
What am I doing wrong?
I've seen the posts on passing GET parameters and hardcoded parameters here and here.
What I am trying to do is pass POST parameters to a custom decorator. The route is not actually rendering a page but rather processing some stuff and sending the results back through an AJAX call.
The decorator looks like this:
# app/util.py
from functools import wraps
from models import data
# custom decorator to validate symbol
def symbol_valid():
def decorator(func):
#wraps(func)
def decorated_function(symbol, *args, **kwargs):
if not data.validate_symbol(symbol):
return jsonify({'status': 'fail'})
return func(*args, **kwargs)
return decorated_function
return decorator
The view looks something like this:
# app/views/matrix_blueprint.py
from flask import Blueprint, request, jsonify
from ..models import data
from ..util import symbol_valid
matrix_blueprint = Blueprint('matrix_blueprint', __name__)
# routing for the ajax call to return symbol details
#matrix_blueprint.route('/route_line', methods=['POST'])
#symbol_valid
def route_line():
symbol = request.form['symbol'].upper()
result = data.get_information(symbol)
return jsonify(**result)
I understand that I can actually call #symbol_valid() when I pass a parameter through GET like this /quote_line/<symbol> but I need to POST.
The question then is how can my decorator access the POSTed variable?
Simple solution. Imported Flask's request module into the util.py module which contains the decorator. Removed the outer function as well.
See code:
# app/util.py
from flask import request # <- added
from functools import wraps
from models import data
# custom decorator to validate symbol
def symbol_valid(func):
#wraps(func)
def decorated_function(*args, **kwargs): # <- removed symbol arg
symbol = request.form['symbol'] # <- paramter is in the request object
if not data.validate_symbol(symbol):
return jsonify({'status': 'fail'})
return func(*args, **kwargs)
return symbol_valid
The decorator accept a func parameter. You must use your decorator like #symbol_valid() or make the function symbol_valid accept a func parameter.
If you are doing it right, you can access the request object anywhere during the request cycle. It just works.