I've been trying to test my login system using the following code
from django.test import TestCase, Client
from rest_framework.test import force_authenticate
class DeckTestCase(TestCase):
#classmethod
def setUp(cls):
test_user1 = User.objects.create(first_name='tester',username='test1', password='123', email='testuser#something.com')
def test_if_logged(self):
factory = Client()
user = User.objects.get(username='teste1')
request = factory.post('/login', {'username': 'test1', 'password': '123'}, format='json')
force_authenticate(request,user=user)
print(request)
But i keep getting this response, which is 401 (Unauthorized)
<Response status_code=401, "application/json">
Can someone help me? I don't know how do I send an auth_token with test_if_logged
You should use .create_user(…) [Django-doc] to create a user, this will set a hashed password for that user:
#classmethod
def setUp(cls):
test_user1 = User.objects.create_user(
first_name='tester',
username='test1',
password='123',
email='testuser#something.com'
)
Related
I'm trying to write a custom authentication middleware for Django channels since I'm using JWT for my app's authentication scheme. I'm using the method that is mentioned in this article which basically gets user's token in the first request that is made to the websockets and then, in the receive method of the consumers.py file, fetches user's data based on that and then pours it in the self.scope['user'] (can't make use of the token_auth.py method because the UI app is separate..). Now since I have NO ACCESS to the request param that is usually being used in the views.py files to get the user's data, is there anyway to get the user's data out of an access token alone??
Hello #Jalal try this.
from channels.auth import AuthMiddlewareStack
from rest_framework.authtoken.models import Token
from django.contrib.auth.models import AnonymousUser
from django.db import close_old_connections
from rest_framework_simplejwt.backends import TokenBackend
class TokenAuthMiddleware:
"""
Token authorization middleware for Django Channels 2
"""
def __init__(self, inner):
self.inner = inner
def __call__(self, scope):
headers = dict(scope['headers'])
if b'authorization' in headers:
try:
token_name, token_key = headers[b'authorization'].decode().split()
if token_name == 'Token':
print(token_key)
valid_data = TokenBackend(algorithm='HS256').decode(token_key,verify=False)
print(valid_data)
scope['user_id'] = valid_data['user_id']
close_old_connections()
except Token.DoesNotExist:
scope['user'] = AnonymousUser()
return self.inner(scope)
TokenAuthMiddlewareStack = lambda inner: TokenAuthMiddleware(AuthMiddlewareStack(inner))
Here valid_data return this:
{'token_type': 'access', 'exp': 1627994463, 'jti':
'192275bd7fd649758838ave1899e1863', 'user_id': 2}
If you want to fetch more data(like:Username,email,etc) through access token then use Customizing token claims:
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer
from rest_framework_simplejwt.views import TokenObtainPairView
class MyTokenObtainPairSerializer(TokenObtainPairSerializer):
#classmethod
def get_token(cls, user):
token = super().get_token(user)
# Add custom claims
token['username'] = user.username
token['email'] = user.email
# ...
return token
class MyTokenObtainPairView(TokenObtainPairView):
serializer_class = MyTokenObtainPairSerializer
I've been stuck on this problem for a couple hours. I'm new to django and automated testing, and I've been playing around with Selenium and django's StaticLiveServerTestCase. I've been trying to test my login form (which works fine when I use runserver and test it myself, by the way.)
Everything is working great, except I can't seem to successfully login my test user. I've narrowed down the break point to django's User.authenticate method.
The User object is created successfully in my setUp and I can confirm that by accessing it's attributes in my test method. However, authenticate fails.
I've looked at the following for help but they didn't get me very far:
Django Unittests Client Login: fails in test suite, but not in Shell
Authentication failed in Django test
Any idea why authenticate fails? Do I need to add something to my settings?
from django.contrib.staticfiles.testing import StaticLiveServerTestCase
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from django.contrib.auth.models import User
from django.contrib.auth import authenticate
class AccountTestCase(StaticLiveServerTestCase):
def setUp(self):
self.selenium = webdriver.Chrome()
super().setUp()
User.objects.create(username='test', email='test#test.com', password='Test1234', is_active=True)
def tearDownClass(self):
self.selenium.quit()
super().tearDown()
def test_register(self):
user = authenticate(username='test', password='Test1234')
if user is not None: # prints Backend login failed
print("Backend login successful")
else:
print("Backend login failed")
user = User.objects.get(username='test')
print(user)
print(user.username) # prints test
print(user.password) # prints Test1234
I found the issue. The User.authenticate() method hashes the password provided. However, I set the password directly when creating the user, which means it was stored as Test1234, so the hashed password provided during authentication did not match 'Test1234', hence the failure.
To properly store a hashed password, you need to use the set_password() method.
Updated setUp code:
def setUp(self):
self.selenium = webdriver.Chrome()
super().setUp()
user = User.objects.create(username='test', email='test#test.com', is_active=True)
user.set_password('Test1234')
user.save()
Using create_superuser resolved the issue. Below code resolves it.
from django.contrib.auth.models import User
from django.contrib.staticfiles.testing import StaticLiveServerTestCase
from django.test import Client
from selenium.webdriver.common.by import By
from selenium.webdriver.firefox.webdriver import WebDriver
import time
from django.contrib.auth import authenticate
#from apps.digital.models import User
class MyTests(StaticLiveServerTestCase):
port = 0
host = 'my host'
def setUp(self):
super(MyTests, self).setUp()
self.selenium = WebDriver()
self.client = Client()
self.user = User.objects.create_superuser(username='test', password='Test1234', email='test#test.com', is_active=True)
self.user.save()
def tearDown(self):
self.selenium.quit()
super(MyTests, self).tearDown()
def test_login(self):
self.user = authenticate(username='test', password='Test1234')
if self.user is not None: # prints Backend login failed
self.user = User.objects.get(username='test')
print(self.user.username) # prints test
print(self.user.password) # prints Test1234
self.login = self.client.login(username='test', password='Test1234')
self.assertEqual(self.login, True)
print("Backend login successful")
self.selenium.get('%s%s' % (self.live_server_url, '/admin/'))
username_input = self.selenium.find_element_by_name("username")
username_input.send_keys(self.user.username)
password_input = self.selenium.find_element_by_name("password")
password_input.send_keys('Test1234')
self.selenium.find_element_by_xpath('//input[#value="Log in"]').click()
time.sleep(1)
else:
print("Backend login failed")
User Authentication is getting passed but when I try to login with the same credentials, the login fails.
from django.contrib.auth.models import User
from django.contrib.staticfiles.testing import StaticLiveServerTestCase
from django.test import Client
from selenium.webdriver.common.by import By
from selenium.webdriver.firefox.webdriver import WebDriver
import time
from django.contrib.auth import authenticate
#from apps.digital.models import User
class MyTests(StaticLiveServerTestCase):
port = 0
host = '<my host>'
def setUp(self):
super(MyTests, self).setUp()
self.selenium = WebDriver()
self.client = Client()
self.user = User.objects.create(username='test', email='test#test.com', is_active=True)
self.user.set_password('Test1234')
self.user.save()
def tearDown(self):
self.selenium.quit()
super(MyTests, self).tearDown()
def test_login(self):
self.user = authenticate(username='test', password='Test1234')
if self.user is not None: # prints Backend login failed
self.user = User.objects.get(username='test')
print(self.user.username) # prints test
print(self.user.password) # prints Test1234
self.login = self.client.login(username='test', password='Test1234')
self.assertEqual(self.login, True)
print("Backend login successful")
self.selenium.get('%s%s' % (self.live_server_url, '/admin/'))
username_input = self.selenium.find_element_by_name("username")
username_input.send_keys(self.user.username)
password_input = self.selenium.find_element_by_name("password")
password_input.send_keys('Test1234')
self.selenium.find_element_by_xpath('//input[#value="Log in"]').click()
time.sleep(1)
else:
print("Backend login failed")
This is my DRF view:
#api_view(['GET'])
#permission_classes([IsAuthenticated])
def check_user(request):
user = request.user
# use user object here
return JSONResponse({})
And this is my unit test for said view:
class CheckUserViewTest(TestCase):
def test_check_user(self):
user = User.objects.create_user('username', 'Pas$w0rd')
self.client.login(username='username', password='Pas$w0rd')
response = self.client.get(reverse('check_user'))
self.assertEqual(response.status_code, httplib.OK)
But I always get a 401 UNAUTHORIZED response from my view. I have logged in the user in my test. What am I doing wrong?
Since you are using Django REST Framework you have to also use DRF's test client called APIClient instead of Django's test client. This happens automagically if you inherit from DRF's APITestCase instead of Django's TestCase.
Complete example:
class CheckUserViewTest(APITestCase):
def test_check_user(self):
user = User.objects.create_user('username', 'Pas$w0rd')
self.assertTrue(self.client.login(username='username', password='Pas$w0rd'))
response = self.client.get(reverse('check_user'))
self.assertEqual(response.status_code, httplib.OK)
An alternative is to use force_authenticate:
class CheckUserViewTest(APITestCase):
def test_check_user(self):
user = User.objects.create_user('username', 'Pas$w0rd')
self.client.force_authenticate(user)
response = self.client.get(reverse('check_user'))
self.assertEqual(response.status_code, httplib.OK)
If your are using djangorestframework you must have to use APITestCase here.
An complete example is just below
from django.urls import reverse
from rest_framework import status
from rest_framework.test import APITestCase
from django.contrib.auth.models import User
from django.contrib.auth.hashers import make_password
class TestLogin(APITestCase):
'''
This will handle login testcases
'''
def setUp(self):
self.url = reverse('check_user')
def test_login(self):
'''
This will test successfull login
'''
data = {
"full_name" : "full name",
'email' : "email#gmail.com",
'password' : "password"
}
User.objects.create(
full_name = data.get('full_name'),
email = data.get('email'),
password = make_password(data.get('password'))
)
response = self.client.get(self.url, data=data)
self.assertEqual(response.status_code,status.HTTP_200_OK)
The following test code does not pass even though manually submitting the form on my web interface actually does work.
import os
from flask.ext.testing import TestCase
from flask import url_for
from config import _basedir
from app import app, db
from app.users.models import User
class TestUser(TestCase):
def create_app(self):
"""
Required method. Always implement this so that app is returned with context.
"""
app.config['TESTING'] = True
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + os.path.join(_basedir, 'test.db')
app.config['WTF_CSRF_ENABLED'] = False # This must be disabled for post to succeed during tests
self.client = app.test_client()
ctx = app.app_context()
ctx.push()
return app
def setUp(self):
db.create_all()
#pass
#app.teardown_appcontext
def tearDown(self):
db.session.remove()
db.drop_all()
#pass
def test_admin_home(self):
# url is the admin home page
url = url_for('admin.index')
resp = self.client.get(url)
self.assertTrue(resp.status_code == 200)
def test_admin_registration(self):
url = url_for('admin.register_view')
data = {'username': 'admin', 'email': 'admin#example.com', 'password': 'admin'}
resp = self.client.post(url, data)
self.assertTrue(resp.status_code == 200)
u = User.query.filter_by(username=u'admin').first()
self.assertTrue(u.username == 'admin') # <----- This fails. Why?
After the test client has post to the register_view url and returns a 200 OK response, I fail to retrieve the 'admin' user from the test database. Why is this so?
Here's the view code (this is a flask admin view)
from flask import request
from flask.ext.admin import expose, AdminIndexView, helpers
from app.auth.forms import LoginForm, RegistrationForm
from app.users.models import User
from app import db
class MyAdminIndexView(AdminIndexView):
#expose('/', methods=('GET', 'POST'))
def index(self):
# handle user login
form = LoginForm(request.form)
self._template_args['form'] = form
return super(MyAdminIndexView, self).index()
#expose('/register/', methods=('GET', 'POST'))
def register_view(self):
# handle user registration
form = RegistrationForm(request.form)
if helpers.validate_form_on_submit(form):
user = User()
form.populate_obj(user)
db.session.add(user)
db.session.commit()
self._template_args['form'] = form
return super(MyAdminIndexView, self).index()
Dumbest mistake ever.
The offending line in my test code is
resp = self.client.post(url, data)
It should be
resp = self.client.post(url, data=data)
I managed to track it down by painstakingly walking through the logic and inserting ipdb.set_trace() step by step until I found the bad POST request made by my client.
I'm trying to access the request.user object when testing my app using django's client class.
from django.test import TestCase
from django.test.client import Client
class SomeTestCase(TestCase):
def setUp(self):
self.client = Client()
self.client.login( username="foo", password="bar")
def test_one(self):
response = self.client.get("/my_profile/")
self.fail( response.request.user )
This will obviously fail, but it fails because response.request doesn't have a user attribute.
AttributeError: 'dict' object has no attribute 'user'
Is there any way to access the user object from the client instance? I want to do this in order to check if some test user's data is properly rendered. I can try to tackle this by setting up all the necessary info during setUp, but before I do that I wanted to check if I'm not just missing something.
This may seem a roundabout way of doing it but it can be useful.
Use RequestFactory, its pretty straightforward. It imitates the request object.
from django.test import TestCase, RequestFactory
from django.test.client import Client
from django.contrib.auth.models import User
class SomeTestCase(TestCase):
def setUp(self):
self.client = Client()
self.factory = RequestFactory()
self.user = User.objects.create_user(
username='foo', email='foo#bar',
password='bar')
def test_one(self):
self.client.login( username="foo", password="bar")
request = self.factory.get("/my_profile/")
request.user = self.user
#rest of your code
def tearDown(self):
self.user.delete()
I hope that was helpful.
Use response.context['user'].
User is automatically available in the template context if you use RequestContext. See auth data in templates doc.
Otherwise i believe you should just query it:
def test_one(self):
response = self.client.get("/my_profile/")
user = User.objects.get(username="foo")
self.fail( user.some_field == False )