Django: set cookie on test client? - python

My Django site is set up with some middleware that on each request, checks for a cookie, and if it is not set, forwards the user elsewhere.
I now want to run some tests on the site. This is my code:
def test_contactform(self):
response = self.client.get('/contact/')
self.assertEqual(response.status_code, 200)
print response
self.assertTrue('Contact me' in response.content)
Unfortunately, this fails with:
Vary: Cookie
Content-Type: text/html; charset=utf-8
Location: http://testserver/ldap/?next=/contact/
Traceback (most recent call last):
File "tests.py", line 43, in test_contactform
self.assertEqual(response.status_code, 200)
AssertionError: 302 != 200
Can I either (i) set a cookie on the Django test client (and if so how) or (ii) require the Django test client to follow the redirect and test against the final page?

None of the above worked for me (Django1.9, Python3.4). Found this solution here:
from django.test import TestCase
from http.cookies import SimpleCookie
class TestViewWithCookies(TestCase):
def test_votes(self):
self.client.cookies = SimpleCookie({'name': 'bla'})
response = self.client.get('/vote/2')
self.assertEqual(response.status_code, 200)

While the accepted answer is the right approach for this problem, I just want to point out that you can set cookies directly (i.e. approach number (i) as you call it), but not via the test client. Instead you need to use a RequestFactory to construct a request which you can set the cookie on, then pass that directly to the view in question.
So instead of:
response = self.client.get('/contact/')
you do:
request = RequestFactory().get('/contact/')
request.COOKIES['thing'] = 'whatever'
response = contact_view(request)
where contact_view is the view serving /contact/.

You can set cookies for test client by calling load on cookies attribute which is SimpleCookie object.
from django.core import signing
self.client.cookies.load({
'example': '123',
'signed_example': signing.get_cookie_signer('signed_example').sign('123')
})
Django's test client is stateful - will persist cookies between tests and will ignore expire dates. For removal, you need to manually remove cookie or create a new client. - See docs
--- For Python 3 and Django 2+

The client.get method takes a follow argument which allows it to follow redirects:
response = self.client.get('/contact/', follow=True)

This is an old question but maybe this is handy for someone:
from http.cookies import SimpleCookie
from django.test import TestCase, Client
class CookieClientTests(TestCase):
def test_cookie(self):
cookies = SimpleCookie()
cookies["cookie_key"] = "something"
client = Client(HTTP_COOKIE=cookies.output(header='', sep='; '))
resp = client.get("/")
self.assertEqual(200, resp.status_code)

Related

Can't access or print any request data with FastAPI

I have a simple FastAPI endpoint, where I want to receive a string value. In this case, I tried it with a JSON body, but basically it doesn't need to be JSON. I really need only a simple string to separate the requests from each other. Unfortunately, I can't access any of the request parameters with a GET method. I also tried POST method instead, but I get an error:
request:
url = "http://127.0.0.1:5000/ping/"
payload=json.dumps({"key":"test"})
headers = {
"Content-Type": "application/json"
}
response = requests.request("POST", url, headers=headers, json=payload)
print(response.text)
api:
#app.get("/ping/{key}")
async def get_trigger(key: Request):
key = key.json()
test = json.loads(key)
print(test)
test2 = await key.json()
print(key)
print(test2)
return
I can't print anything with post or put:
#app.post("/ping/{key}")
async def get_trigger(key: Request):
...
or
#app.put("/ping/{key}")
async def get_trigger(key: Request):
I get a 405 Method not allowed error.
How can I get this fixed?
The 405 Method Not Allowed status code indicates that "the server knows the request method, but the target resource doesn't support this method". You get this error when you attempt, for instance, to send a POST request to a GET route (as shown in your first example). This, however, is not the only issue with your code (on both client and server sides). Below is given an example on how to achieve what you described in the question using Path parameters. The same could be achieved using Query parameters, as well as Request Body. Please have a look at Python requests documentation on how to specify the parameters/body for each case. I would also highly suggest to take the FastAPI tutorial online—you'll find most of the answers you are looking for there.
app.py
from fastapi import FastAPI
app = FastAPI()
#app.get("/ping/{ping_id}")
async def get_trigger(ping_id: str):
return {"ping_id": ping_id}
test.py
import requests
url = 'http://127.0.0.1:8000/ping/test1'
resp = requests.get(url=url)
print(resp.json())

Django Test Client client.post() sends GET request in my Testcase

How can I submit a POST request with Django test Client? I just want to test the login function of my application. It's strange that I put
response = self.client.post(reverse('rbac:login'), data=data) in my testcase, and I want to validate it so I print response.status_code.Actually it should return 302 coz it should turn to another page. but it return 200. This situation happens when I use "python manage.py test testmodel" to test. However, if I use "python manage.py shell" to make an InteractiveConsole and write exactly the same code, it return 302 at last.
I have nearly tried all the methods provided from stack overflow, e.g. change the url, offer the content-type when I post....but these are meaningless.
from django.test import TestCase
from django.test.utils import setup_test_environment,teardown_test_environment
from django.test import Client
from django.urls import reverse
import django
from rbac.models import RbacUser, RbacRole
from system_module.models import Redeploy
from urllib import urlencode
class CreateUser(TestCase):
#classmethod
def setUp(self):
self.client = Client()
def test_create_user(self):
data = {"Username": "admin_xy", "Password": "aaa"}
response = self.client.post(reverse('rbac:login'), data=data)
print response
and it shows me 200.....
However, in interactive console:
>>> response = client.post(reverse('rbac:login'), data=data)
>>> print response.status_code
302
it shows the right result.
I want to know how to cope with this annoying problem. Thx
I expect when I use test file to test my test case, it works fine....like show 302 code.
Before sending the POST or GET request by using Client, first you need to create the user make the user active and give necessary permissions and login through client and then send the post request.
Ex: self.client.login(username='username', 'password'='password)
It's been long time that this question is here, I hope you found the answer, but for other people I'll suggest to add this to this code to be redirected:
response = self.client.post(reverse('rbac:login'), data=data, follow=True)
The follow=True will follow the redirect .

How to test `#authenticated` handler using tornado.testing?

I am new to unit testing using script. I tried to verify login with arguments in post data, but I am getting login page as response and not get logged in.Because of #tornado.web.authenticated i can't access other functions without login and it responding to login page
import tornado
from tornado.testing import AsyncTestCase
from tornado.web import Application, RequestHandler
import app
import urllib
class MyTestCase(AsyncTestCase):
#tornado.testing.gen_test
def test_http_fetch_login(self):
data = urllib.urlencode(dict(username='admin', password=''))
client = AsyncHTTPClient(self.io_loop)
response = yield client.fetch("http://localhost:8888/console/login/?", method="POST",body=data)
# Test contents of response
self.assertIn("Automaton web console", response.body)
#tornado.testing.gen_test
def test_http_fetch_config(self):
client = AsyncHTTPClient(self.io_loop)
response = yield client.fetch("http://localhost:8888/console/configuration/?")
self.assertIn("server-version",response.body)
To test code that uses #authenticated (unless you are testing the redirection to the login page itself), you need to pass a cookie (or whatever form of authentication you're using) that will be accepted by your get_current_user method. The details of this will vary depending on how exactly you are doing your authentication, but if you're using Tornado's secure cookies you'll probably use the create_signed_value function to encode a cookie.
From documentation:
If you decorate post() methods with the authenticated decorator, and the user is not logged in, the server will send a 403 response. The #authenticated decorator is simply shorthand for if not self.current_user: self.redirect() and may not be appropriate for non-browser-based login schemes.
So you can use mock do like pointed in this answer: https://stackoverflow.com/a/18286742/1090700
Another 403 status will be thrown on POST calls if you have secure_cookies=True app param. If you want to test the code in a non debug fashion, you can also using mock POST actions while keeping the secure_cookies=True application parameter and then just mock the check_xsrf_cookie Handler method as well, like this:
# Some previous stuff...
with mock.patch.object(YourSecuredHandler, 'get_secure_cookie') as mget:
mget.return_value = 'user_email'
response = self.fetch('/', method='GET')
with mock.patch.object(
YourSecuredHandler, 'check_xsrf_cookie') as mpost:
mpost.return_value = None
response = self.fetch(
url, method="POST", body=parse.urlencode(body1),
headers=headers)
self.assertEqual(response.code, 201)

How can I unit test django messages... with a HTTPResponseRedirect?

Just like this question... but harder.
I have a view that redirects a user, and uses the Django messages framework to send them to the right page, and adds a message with code like that below:
def new_comment(request,pid):
post = get_object_or_404(DiscussionPost,pk=pid)
if post.closed:
messages.error(request, _('This post is closed. Your comment was not added.'))
return HttpResponseRedirect(reverse("discussionsPost",args=[post.pk]))
Now this works well for a user, but when testing the messages aren't available.
In the unit test I do:
response = self.client.post(reverse('aristotle:discussionsPostNewComment',args=[p1.id]),
{'body':"Post is closed, so I can NOT comment."}
)
#the below assertion passes
self.assertRedirects(response,reverse('aristotle:discussionsPost',args=[p1.id]))
print response
print response.context['messages']
And the first print gives:
Vary: Accept-Language, Cookie
X-Frame-Options: SAMEORIGIN
Content-Type: text/html; charset=utf-8
Location: http://testserver/discussions/post/1
Content-Language: en
With the second failing with the error:
Traceback (most recent call last):
File "/home/ubuntu/workspace/aristotle_mdr/tests/main/test_discussions.py", line 393, in test_post_to_closed_discussion
print response.context['messages']
TypeError: 'NoneType' object has no attribute '__getitem__'
Plus there its not possible to use messages.get_messages as there is no request item that can be used.
Since, there is no context dictionary in HTTPResponseRedirect, how can I check whether the message was sent properly?
If you want to test the response after the redirect, then you need to tell the Django test client to "follow" the redirect chain, via the follow argument. As stated in the Django documentation:
If you set follow to True the client will follow any redirects and a redirect_chain attribute will be set in the response object containing tuples of the intermediate urls and status codes.
So your test post would need to look something like:
response = self.client.post(
reverse('aristotle:discussionsPostNewComment', args=[p1.id]),
{'body':"Post is closed, so I can NOT comment."},
follow=True
)

Web proxy in python/django?

I need to have a proxy that acts as an intermediary to fetch images. An example would be, my server requests domain1.com/?url=domain2.com/image.png and domain1.com server will respond with the data at domain2.com/image.png via domain1.com server.
Essentially I want to pass to the proxy the URL I want fetched, and have the proxy server respond with that resource.
Any suggestions on where to start on this?
I need something very easy to use or implement as I'm very much a beginner at all of this.
Most solutions I have found in python and/or django have the proxy acts as a "translater" i.e. domain1.com/image.png translates to domain2.com/image.png, which is obviously not the same.
I currently have the following code, but fetching images results in garbled data:
import httplib2
from django.conf.urls.defaults import *
from django.http import HttpResponse
def proxy(request, url):
conn = httplib2.Http()
if request.method == "GET":
url = request.GET['url']
resp, content = conn.request(url, request.method)
return HttpResponse(content)
Old question but for future googlers, I think this is what you want:
# proxies the google logo
def test(request):
url = "http://www.google.com/logos/classicplus.png"
req = urllib2.Request(url)
response = urllib2.urlopen(req)
return HttpResponse(response.read(), mimetype="image/png")
A very simple Django proxy view with requests and StreamingHttpResponse:
import requests
from django.http import StreamingHttpResponse
def my_proxy_view(request):
url = request.GET['url']
response = requests.get(url, stream=True)
return StreamingHttpResponse(
response.raw,
content_type=response.headers.get('content-type'),
status=response.status_code,
reason=response.reason)
The advantage of this approach is that you don't need to load the complete file in memory before streaming the content to the client.
As you can see, it forwards some response headers. Depending on your needs, you may want to forward the request headers as well; for example:
response = requests.get(url, stream=True,
headers={'user-agent': request.headers.get('user-agent')})
If you need something more complete than my previous answer, you can use this class:
import requests
from django.http import StreamingHttpResponse
class ProxyHttpResponse(StreamingHttpResponse):
def __init__(self, url, headers=None, **kwargs):
upstream = requests.get(url, stream=True, headers=headers)
kwargs.setdefault('content_type', upstream.headers.get('content-type'))
kwargs.setdefault('status', upstream.status_code)
kwargs.setdefault('reason', upstream.reason)
super().__init__(upstream.raw, **kwargs)
for name, value in upstream.headers.items():
self[name] = value
You can use this class like so:
def my_proxy_view(request):
url = request.GET['url']
return ProxyHttpResponse(url, headers=request.headers)
The advantage of this version is that you can reuse it in multiple views. Also, it forwards all headers, and you can easily extend it to add or exclude some other headers.
If the file you're fetching and returning is an image, you'll need to change the mimetype of your HttpResponse Object.
Use mechanize, it allow you to choose a proxy and act like a browser, making it easy to change the user agent, to go back and forth in the history and to handle authentification or cookies.

Categories

Resources