Error in pytest with django response codes - django

I am using pytest to test my django rest framework API and am gettin gan error on the following test:
def test_client_gets_invalid_when_submitting_invlaid_data(self):
client = APIClient()
response = client.post(path="/user/register/", data={})
assert response.status_code is 400
traceback in pytest is as follows:
> assert response.status_code is 400
E assert 400 is 400
E + where 400 = <Response status_code=400, "application/json">.status_code
core\tests\test_views.py:26: AssertionError
I dont understand how this error can be happening when 400 is literally equal to 400?

So it turns out that the keyword is tests for identity in memory, where as == tests for value.
So the solution was to simply change from is to ==
https://www.geeksforgeeks.org/python-object-comparison-is-vs/

The is operator compares the identity of the two objects, not their value. In this case, response.status_code is an int object, and 400 is also an int object, but they are different instances of int objects.
You should use the == operator instead to compare their values.
def test_client_gets_invalid_when_submitting_invlaid_data(self):
client = APIClient()
response = client.post(path="/user/register/", data={})
assert response.status_code == 400

Related

POST request parameters returning "none"

I have the following POST request:
import requests
payload = {'key1':'value1'}
r = requests.post('http://127.0.0.1:5000/test', params=payload)
print(r.url)
print(r.text)
My flask app tries to return the value from key1:
from flask import Flask, request
app = Flask(__name__)
#app.route('/test', methods = ["GET", "POST"])
def query_params():
val = request.args.get("key1")
return val
Going to http://127.0.0.1:5000/test returns
TypeError TypeError: The view function for 'query_params' did not
return a valid response. The function either returned None or ended
without a return statement.
Output from flask debugger:
127.0.0.1 - - [21/May/2022 21:47:17] "POST /test?key1=value1 HTTP/1.1" 200 -
What am I missing here? Thank you very much for your help!
Cheers,
Mario
when you visit the http://127.0.0.1:5000/test from your browser, its a GET request and there are no parameters passed in your request.
if you visit http://127.0.0.1:5000/test?key1=value1, your expected output will be printed.
Regarding the requests.post snippet you used: if you see the documentation, params is usually used in GET requests, the POSTS get the data argument. but seems your code works, it appends the parameters to the request (as would have happened in GET) and makes a POST request. Interesting finding!
r = requests.post('http://127.0.0.1:8000/test', data=payload)
you could enhance your code by using a "fallback" value if the parameter is not present:
#app.route('/test', methods = ["GET", "POST"])
def query_params():
val = request.args.get("key1", "parameter was not provided")
return val
To conclude, i think you should decide if the request method to submit the data should be a GET or a POST, and then update your code accordingly (if GET, your snippets is OK, if you should use POST, try to switch the params to data and then your flask route code to work with the new payload "format".
updated code to "launch a python script if the flask app receives a POST request with a specific key:value pair":
#app.route('/test', methods = ["GET", "POST"])
def query_params():
if request.method == 'POST':
val = request.args.get("the expected key", "parameter was not provided")
if val == "the expected value":
# do the things you want to do
return "processing done!"

flask restx - pytest in not working with error handler

I am building API using Flask Restx. I had written a generic exception handler which will throw an error message and code in Json format.
#app.errorhandler(APIError)
def handle_invalid_usage(error):
logger.opt(exception=True).error(error)
response = jsonify(error.to_dict())
response.status_code = error.status_code
return response
I am trying to write unit test using pytest for testing error condition(Resource Not Found). My unit test does return only 500 Internal server error (response.status_code) instead of the going through the error handler and returning 404. When I test it directly using Postman it works perfect.
#pytest.fixture(scope="session")
def app():
app = create_app()
return app
#pytest.fixture
def client(app):
return app.test_client()
def test_get_sequence_resource_not_found(mocker: MockerFixture, client: FlaskClient):
mocked = mocker.patch("service.ViewSequenceService.get_sequence")
mocked.side_effect = ResourceNotFound('Sequence id Not Present')
response = client.get("/sequence/f15a9f6e-7a1d-4d12-99c3-fb80d4bc98a1")
json_data = json.loads(response.data)
assert response.status_code == 404
assert json_data['payload']['message'] == 'Sequence id Not Present'
assert json_data['payload']['status'] == 404
Was running into the same issue. It seems that for pytest to recognize it the exception has to registered with the API instead of the app.
from flask_restx import Api
class APIError(Exception):
status_code = 400
content = {"error": "Bad request"}
api = Api()
#api.errorhandler(APIError)
def handle_invalid_usage(exc: APIException):
return exc.content, exc.status_code

Django Tests: increase testsRun counter

In some Django Tests I have loop to test many things.
in the end-result it shows up as:
Ran 1 test in 3.456s
I would like to increment that counter for each loop, how can I do that?
It is using subTest() , but that does not update the counter (which I believe is a parameter testsRun)
my test looks something like this
class MyTestCase(TestCase):
def test_auth_pages(self):
pages = ['homepage', 'dashboard', 'profile']
for page in pages:
with self.subTest():
# ....testsRun += 1
self.c.login(username='test', password='test')
response = self.c.get(reverse_lazy(page))
self.assertEqual(200, response.status_code, msg=page)
self.c.logout()
response = self.c.get(reverse_lazy(page))
self.assertEqual(302, response.status_code, msg=page)
If you don't mind changing testing framework, consider pytest with pytest-django package. You can easily parametrize a test using #pytest.mark.parametrize:
import pytest
#pytest.mark.parametrize("page_name", ['homepage', 'dashboard', 'profile'])
def test_some_page(page_name, client):
client.login(username='test', password='test')
response = client.get(reverse_lazy(page))
assert response.status_code == 200
client.logout()
response = client.get(reverse_lazy(page))
assert response.status_code == 302
If not, you could create a test function factory that would accept the page name and return a test function for that page:
class MyTestCase(TestCase):
def _create_page_test(page_name):
def test_function(self):
self.c.login(username='test', password='test')
response = self.c.get(reverse_lazy(page_name))
self.assertEqual(200, response.status_code, msg=page_name)
self.c.logout()
response = self.c.get(reverse_lazy(page_name))
self.assertEqual(302, response.status_code, msg=page_name)
return test_function
test_homepage = _create_page_test("homepage")
test_dashboard = _create_page_test("dashboard")
test_profile = _create_page_test("profile")
The added benefit of such changes is that each page has a separate test, independent from the other. That makes debugging easier.
You can achieve this with a different test suite.
Check out test generators from the django-nose package
def test_evens():
for i in range(0, 5):
yield check_even, i, i*3 # this generates 5 different tests
def check_even(n, nn):
assert n % 2 == 0 or nn % 2 == 0

Client patch gives 400 error in django testing

So basically when i try to test my patch view it doesn't patch at all as i get a 400 error. However, when i call the same patch view outside the APITestCase, while running the test server, it does work.
what i've tried:
patch_data = json.dumps({'first_name': 'test999'})
format='json'
At APITestCase:
def test_patch_update_data(self):
self.client.force_authenticate(user=self.user)
self.assertEqual(self.user.first_name, 'Robert')
patch_data = {'first_name': 'test999'}
response = self.client.patch('http://testserver/api/patch/{0}/'.format(
self.user.id), patch_data, format='json')
self.assertEqual(response.status_code, 200) # ERROR:GIVES 400!
self.assertEqual(self.user.first_name, 'test999')
At views.py
class UsuariosUpdatePatchAPIView(UpdateAPIView):
queryset = Usuarios.objects.all()
serializer_class = UsuariosUpdateSerializer
permission_classes = [IsAuthenticated, IsSelfUser]
The error:
self.assertEqual(response.status_code, 200)
AssertionError: 400 != 200
Thanks in advance! :)
How silly i am:
As stated in the comments, response.data gives very useful information. It turned out to be that the field fist_name only accepted letters! So the solution was changing test999 to something without numbers!

Django rest framework API calling to another API [duplicate]

Is there any way to make a RESTful api call from django view?
I am trying to pass header and parameters along a url from the django views. I am googling from half an hour but could not find anything interesting.
Any help would be appreciated
Yes of course there is. You could use urllib2.urlopen but I prefer requests.
import requests
def my_django_view(request):
if request.method == 'POST':
r = requests.post('https://www.somedomain.com/some/url/save', params=request.POST)
else:
r = requests.get('https://www.somedomain.com/some/url/save', params=request.GET)
if r.status_code == 200:
return HttpResponse('Yay, it worked')
return HttpResponse('Could not save data')
The requests library is a very simple API over the top of urllib3, everything you need to know about making a request using it can be found here.
Yes i am posting my source code it may help you
import requests
def my_django_view(request):
url = "https://test"
header = {
"Content-Type":"application/json",
"X-Client-Id":"6786787678f7dd8we77e787",
"X-Client-Secret":"96777676767585",
}
payload = {
"subscriptionId" :"3456745",
"planId" : "check",
"returnUrl": "https://www.linkedin.com/in/itsharshyadav/"
}
result = requests.post(url, data=json.dumps(payload), headers=header)
if result.status_code == 200:
return HttpResponse('Successful')
return HttpResponse('Something went wrong')
In case of Get API
import requests
def my_django_view(request):
url = "https://test"
header = {
"Content-Type":"application/json",
"X-Client-Id":"6786787678f7dd8we77e787",
"X-Client-Secret":"96777676767585",
}
result = requests.get(url,headers=header)
if result.status_code == 200:
return HttpResponse('Successful')
return HttpResponse('Something went wrong')
## POST Data To Django Server using python script ##
def sendDataToServer(server_url, people_count,store_id, brand_id, time_slot, footfall_time):
try:
result = requests.post(url="url", data={"people_count": people_count, "store_id": store_id, "brand_id": brand_id,"time_slot": time_slot, "footfall_time": footfall_time})
print(result)
lJsonResult = result.json()
if lJsonResult['ResponseCode'] == 200:
print("Data Send")
info("Data Sent to the server successfully: ")
except Exception as e:
print("Failed to send json to server....", e)