django pytest how to test a view with argument(id) - python

i have a question regarding using pytest. These are my very 1st tests. I have 2 views which
i want to test (simplest possible way).
Views:
class MenuView(View):
def get(self, request):
return render(request, 'diet_app/menu.html')
class CuisineDetailsView(View):
def get(self, request, id):
cuisine = Cuisine.objects.get(id=id)
recipes = cuisine.recipe_set.all()
return render(request, 'diet_app/cuisine_details.html', {'cuisine': cuisine, 'recipes': recipes})
Here are my tests:
def test_menu(client):
url = reverse('menu')
response = client.get(url)
assert response.status_code == 200
#pytest.mark.django_db
def test_cuisine_details_view(client):
url = reverse('cuisine-details')
response = client.get(url)
assert response.status_code == 200
Urls:
path('menu/', MenuView.as_view(), name='menu'),
path('cuisine_details/<int:id>/', CuisineDetailsView.as_view(), name='cuisine-details'),
1st test (menu view) is working properly
2nd test (cuisine details view) shows error
.NoReverseMatch: Reverse for 'cuisine-details' with no arguments not found. 1 pattern(s) tried: ['cuisine_details\\/(?P<id>
I know i should probably put somethere ID argument but tried few options and havent succeed. Will be grateful for any help/advise

You must pass the id as argument to the reverse function.
#pytest.mark.django_db
def test_cuisine_details_view(client):
url = reverse('cuisine-details', kwargs={'id': 123})
response = client.get(url)
assert response.status_code == 200

Related

Unresolved Reference "content" in JSONResponse

So, I've been trying to write unit tests for a function that returns a JSONResponse that contains a status_code and content. The function looks like this:
def exception_handler(request):
return JSONResponse(
status_code=status.HTTP_404_NOT_FOUND,
content=jsonable_encoder({"detail": NotFoundException} if is_development() else {}),
)
I'm testing for two cases: is_development() returns True or False. When I'm calling the function to test it like this: response = exception_handler(request) I can access response.status_code but not response.content. Why is that? I wanna access the content in order to check if it is correct.
I cannot really reproduce your problem. Here is the code which works in my case.
# ---- Some setup, to make your example work ----
from fastapi.encoders import jsonable_encoder
class status:
HTTP_404_NOT_FOUND = 404
class JSONResponse:
def __init__(self, status_code, content):
self.status_code = status_code
self.content = content
# ----- Here your code -----
def exception_handler(request):
return JSONResponse(
status_code=status.HTTP_404_NOT_FOUND,
content=jsonable_encoder({"detail": None} if is_development() else {}),
)
def is_development():
"""True and False work"""
return False
print(vars(exception_handler(None)))
Output:
{'status_code': 404, 'content': {}}
Could you provide more information?

django-pytest - RequestFactory.get or client.get

thanks for your time.
i'd like to know when testing views is it better to create a request object using RequestFactory().get() or Client().get() or calling the view directly
RequestFactory():
from django.test import RequestFactory
from django.urls import reverse
def test_profile_view(self):
p1 = mixer.blend(Profile)
path = reverse('profile', kwargs={'pk': p1.id})
request = RequestFactory().get(path)
request.user = p1.user
response2 = views.profile_view(request, pk=p1.id)
assert response.status_code == 200
assert response.streaming == False
assert response2.charset == 'utf-8'
assert response2.status_code == 200
Client():
from django.test import Client
class TestView():
def test_profile_view(self):
p1 = mixer.blend(Profile)
path = reverse('profile', kwargs={'pk': p1.id})
response= Client().get(path)
response.user = p1.user
response2 = views.profile_view(request, pk=p1.id)
assert response.status_code == 200
assert response.streaming == False
assert response2.charset == 'utf-8'
assert response2.status_code == 200
and would like to understand the difference please
If you use the RequestFactory and call the view, then you avoid the middlewares. This has the benefit that less codes gets executed and your test is faster.
But it has the drawback, that things might be a bit different on the production site, since on prod the middlewares get called.
I use Django since several years, and I am unsure again and again what is better. It makes no big difference.
BTW: This looks strange: response.user = p1.user

redirecting on flask from a 'POST' route to a 'GET' route

I want to redirect to another url( of a route whose method by default is 'GET') from a route whose method has been set to 'POST' if some condition is met. The 'GET' route gets called when I check from the terminal, but the redirection doesn't happen from the browser. Am doing this in flask using 'url_for'. Any help?
I've tried using the _method parameter in the url_for ,i.e, return redirect(url_for('second', _method='GET')
Here's some code:
#app.route('/first', methods=['POST'])
def first():
some_data = request.get_json(force=True)
if some_data is None:
return redirect(url_for('second', _method='GET'))
session['data_to_use'] = some_data
return jsonify(some_data)
#app.route('/second')
def second():
return render_template('second.html')
This is what I get on the terminal:
"POST /first HTTP/1.1" 302 -
"GET /second HTTP/1.1" 200 -
Are you sure your "some_data" value is None? Redirect usually should work just with ordinary url_for usage even if you are redirecting from post to get, like this:
#app.route('/first', methods=['POST'])
def first():
some_data = request.get_json(force=True)
if some_data is None:
return redirect(url_for('second'))
session['data_to_use'] = some_data
return jsonify(some_data)
You can just return the name of the function that renders the template
#app.route('/first', methods=['POST'])
def first():
some_data = request.get_json(force=True)
if some_data is None:
return second()
session['data_to_use'] = some_data
return jsonify(some_data)

Flask-restful basic Authentication

I am new to Flask and I need some help for my school work.
I am trying to build a simple ToDo list system using flask-restful.
My current code looks like this:
class ToDoList(Resource):
'''TODO LIST'''
operation = ['delete']
decorators = [auth.login_required, advertise('operation')]
def post(self):
"""remove all item in the TODO list"""
operation = request.args.get('op')
if operation == 'delete':
collection2.delete_many({})
return {'Success': 'OK'}, 200
return {'Error':'Illegal Operation'}, 400
def get(self):
"""return a list of the TODO name"""
list_1 = collection2.find()
list_2 = []
for each in list_1:
list_2.append(JSONEncoder().encode(each))
return {'list':list_2}, 200
It works, but I want only the post method to require authentication, and get method without authentication so anyone can acquire the list without login. I am using the flask-restful I don't know how to give the decorators separately to each function.
I used flaskrestplus to do basic authentication. All the required authorizations are provided as an authorizations dictionary. Then they are passed to the API.
Also the authorizations can be applied at the method level using
#api.doc(security='basicAuth')
The validation logic (can be ldap validation or db validation) can be writted in a decorator called requires_Auth. This decorator is invoked using
decorators = [requires_Auth]
Complete code
from flask import Flask, request
from flask_restplus import Api, Resource
from functools import wraps
def requires_Auth(f):
#wraps(f)
def decorator(*args, **kwargs):
auth = request.authorization
if auth:
print "inside decorator", auth.username,auth.password
return f(*args, **kwargs)
else:
return "Login required!!!!",401
return decorator
authorizations = {
'basicAuth': {
'type': 'basic',
'in': 'header',
'name': 'Authorization'
}
}
api = Api(app, version='1.0',
authorizations=authorizations
)
ns = api.namespace('/', description='Authentication API')
#ns.route('/withDecorator')
class HelloWorldWithDecorator(Resource):
decorators = [requires_Auth]
#api.doc(security='basicAuth')
def get(self):
return {'hello': 'world'}
api.add_namespace(ns)
if __name__ == '__main__':
app.run(debug=True, host='0.0.0.0', port=5001)
From Flask-RESTful documentation [1]:
Alternatively, you can specify a dictionary of iterables that map to HTTP methods and the decorators will only apply to matching requests.
def cache(f):
#wraps(f)
def cacher(*args, **kwargs):
# caching stuff
return cacher
class MyResource(restful.Resource):
method_decorators = {'get': [cache]}
def get(self, *args, **kwargs):
return something_interesting(*args, **kwargs)
def post(self, *args, **kwargs):
return create_something(*args, **kwargs)
In your case it would be:
method_decorators = {'post': [auth.login_required]}

Django unit test. Simple example

I learn unit test with Django. How to write test for this function? I need this example to understand.
#login_required
def datas(request):
queryset = Data.objects.filter(user=request.user)
if queryset.count() == 0:
return redirect('/data/')
return render_to_response('data_list.html',
{'data': queryset},
context_instance=RequestContext(request))
#imports here
class YourTestCase(TestCase):
fixtures = ['user-data.json']
def setUp(self):
self.client = Client()
def test_empty_datas(self):
self.client.login(username='something', password='something')
response = self.client.get('/path/to/view/') # or reverse by name
self.assertEqual(response.status_code, 302,
'View did not redirect on empty queryset.')
def test_populated_datas(self):
self.client.login(username='something', password='something')
Data.objects.create(some_field=some_value)
response = self.client.get('/path/to/view/') # or reverse by name
self.assertEqual(response.status_code, 200,
'View did not return a 200.')
...and so on. user-data would need to contain at least one user, otherwise you won't be able to authenticate.

Categories