Create authenticated user for django tests - python

The reason I am asking is I have the fields username,password and otp_token. The otp_token is challenging to create, therefore I was wondering if there is a way create an authenticated user at the beginning of the test file to carry out the rest of the django tests as an authenticated user?
For example, how to pass a logged in user to the following
def some_test(self):
login = self.client.login(username='testUser', password='testPassword')
response = self.client.get(reverse('page1:conent1'))
self.assertEqual(response.status_code, 200)
related question

Not sure how are you generating that token, but I think you can use some dummy data in test
from django.test import TestCase, Client
def setUp(self):
self.user = User.objects.create(username='<USERNAME>',
email='<EMAIL>', otp_token='<YOUR_VALUE>')
self.user.set_password(<PASSWORD>)
self.user.save()
self.client = Client()
def some_test(self):
login = self.client.login(username='<USERNAME>', password='<PASSWORD>')
...

Related

Django tests not isolated when testing authentication

I am testing my authentication with the django.test.Client and two tests cases fail because once I test my test_login_success test case, the other tests fail because the user remains authenticated, even when I am instantiating a new client in the class setUp and even deleting the user in the tearDown.
My code:
from django.test import Client, TestCase
from app.users.models import User
class TestLogin(TestCase):
def setUp(self):
super().setUp()
self.email = 'test#test.com'
self.password = 'SomeRandomPass96'
User.objects.create_user(email=self.email, password=self.password)
self.client = Client()
def tearDown(self):
User.objects.filter(email=self.email).delete()
super().tearDown()
def test_not_authenticated(self): # success the first time, fails after test_login_success is executed for the first time.
user = User.objects.get(email=self.email)
assert not user.is_authenticated
def test_login_success(self): # always success
self.client.post(
'/users/login/',
{'email': self.email, 'password': self.password}
)
user = User.objects.get(email=self.email)
assert user.is_authenticated
def test_login_wrong_credentials(self): # success the first time, fails after test_login_success is executed for the first time.
self.client.post(
'/users/login/',
{'email': self.email, 'password': 'wrongPassword123'}
)
user = User.objects.get(email=self.email)
assert not user.is_authenticated
Maybe you can use the method logout from Client class.
this method "Log out the user by removing the cookies and session object."
Tell me if that works.
The error: user.is_authenticated is not doing what is expected in this code. Since the user in request object in the view will be an AnonymousUser or a User instance depending on the case if it's authenticated and I was accessing directly to a User instance from the database.
The proper way is to access to the user from the request object, and since we don't have the view context in the test case, Django provides it returned in the response like so:
def test_not_authenticated(self):
response = self.client.get('')
user = response.wsgi_request.user
assert not user.is_authenticated

Test login protected page with permissions in django (pytest)

I am trying to test a view which only shows a template thus far. To access the view the user needs the permission 'projects.can_view'.
I am trying to create a user with this permission in the test db using a fixture, then I am trying to log a client in with the username and password of my user in the db. I try to visit the view but I get a 302 error (redirect to login view.) I can verify that I actually pass the permissions correctly:
My fixture:
#pytest.fixture()
def create_users_with_permissions():
User = get_user_model()
u = User.objects.create(
username="test",
password="pass",
)
permission = Permission.objects.get(name='Can view project')
u.user_permissions.add(permission)
u = User.objects.get(username='test')
print(u.has_perm('appname.view_project'))
return u
I query the user again to delete the permissions cache like explained in the doc (https://docs.djangoproject.com/en/3.0/topics/auth/default/). When I print out print(u.has_perm('appname.view_project')) it returns True. Thus I assume I have a user with this specific permission created.
Now I pass the fixture to my test and log pytests client-fixture in:
def test_xyz(
self, client, create_users_with_permissions
):
client.login(
username=create_users_with_permissions.username,
password=create_users_with_permissions.password
)
url = reverse('urlforview')
response = client.get(url)
assert response.status_code == 200
This fails with error code: assert 302 == 200
If I create a superuser in the db and log in with the respective user it works. When I hardcode username and password it also fails. What am I missing here?
Thanks for every help or suggestion, very much appreciated!
My view only has this:
class ProjectCustomerList(PermissionRequiredMixin, TemplateView):
template_name = 'mytemplate.html'
permission_required = 'projects.can_view'

Django login_required unittesting [duplicate]

I have created users for my unit tests in two ways:
1) Create a fixture for "auth.user" that looks roughly like this:
{
"pk": 1,
"model": "auth.user",
"fields": {
"username": "homer",
"is_active": 1,
"password":
"sha1$72cd3$4935449e2cd7efb8b3723fb9958fe3bb100a30f2",
...
}
}
I've left out the seemingly unimportant parts.
2) Use 'create_user' in the setUp function (although I'd rather keep
everything in my fixtures class):
def setUp(self):
User.objects.create_user('homer', 'ho...#simpson.net', 'simpson')
Note that the password is simpson in both cases.
I've verified that this info is correctly being loaded into the test database time and time again. I can grab the User object using User.objects.get. I can verify the password is correct using 'check_password.' The user is active.
Yet, invariably, self.client.login(username='homer', password='simpson') FAILS. I'm baffled as to why. I think I've read every single Internet discussion pertaining to this. Can anybody help?
The login code in my unit test looks like this:
login = self.client.login(username='homer', password='simpson')
self.assertTrue(login)
Thanks.
The code that doesn't work:
from django.contrib.auth.models import User
from django.test import Client
user = User.objects.create(username='testuser', password='12345')
c = Client()
logged_in = c.login(username='testuser', password='12345')
Why doesn't it work?
In the snippet above, when the User is created the actual password hash is set to be 12345. When the client calls the login method, the value of the password argument, 12345, is passed through the hash function, resulting in something like
hash('12345') = 'adkfh5lkad438....'
This is then compared to the hash stored in the database, and the client is denied access because 'adkfh5lkad438....' != '12345'
The Solution
The proper thing to do is call the set_password function, which passes the given string through the hash function and stores the result in User.password.
In addition, after calling set_password we must save the updated User object to the database:
user = User.objects.create(username='testuser')
user.set_password('12345')
user.save()
c = Client()
logged_in = c.login(username='testuser', password='12345')
An easier way is to use force_login, new in Django 1.9.
force_login(user, backend=None)
For example:
class LoginView(TestCase):
def setUp(self):
self.client.force_login(User.objects.get_or_create(username='testuser')[0])
Check that django.contrib.sessions is added to INSTALLED_APPS because client.login() checks that it is and will always return false if it is not:
https://docs.djangoproject.com/es/1.9/topics/http/sessions/#enabling-sessions
Can you check like below,
from django.test import TransactionTestCase, Client
class UserHistoryTest(TransactionTestCase):
self.user = User.objects.create(username='admin', password='pass#123', email='admin#admin.com')
self.client = Client() # May be you have missed this line
def test_history(self):
self.client.login(username=self.user.username, password='pass#123')
# get_history function having login_required decorator
response = self.client.post(reverse('get_history'), {'user_id': self.user.id})
self.assertEqual(response.status_code, 200)
This test case worked for me.
If you are using rest_framework, make sure session-based authentication is enabled. That was my issue.
Go to your settings.py file and check that REST_FRAMEWORK -> DEFAULT_AUTHENTICATION_CLASSES includes SessionAuthentication:
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": [
"rest_framework.authentication.TokenAuthentication",
"rest_framework.authentication.SessionAuthentication",
],
...
}
It looks like the login method uses the vanilla Django session-based approach, so if you were only using rest_framework's token auth that's going to fail.
from django.test import TestCase
from django.contrib.auth.models import User
from django.test import Client
class MyProfile(TestCase):
#classmethod
def setUpClass(self):
self.username = 'dummy' + data + '#gmail.com'
self.password = 'Dummy#123'
user = User.objects.create(username=self.username)
user.set_password(self.password)
user.save()
c = Client()
self.client_object = c.login(username=self.username, password=self.password)
self.content_type = "application/json"
response = self.client_object.post('/api/my-profile/', content_type=self.content_type)
If you just need to have an authenticated user during testing the test cases you can use force_login which does not need any authentication properties just pass the user object.
def test_something_view(self):
client = Client()
client.force_login(self.user)
response = client.post(reverse('your custom url'), follow=True)
self.assertEqual(response.status_code, 200)
If anyone still following this , I think the attributes 'is_staff' and 'is_active' should be kept True for successfully logging in......
self.user = User.objects.create(username='testuser',password='pwd',is_active=1,is_staff=1)

django how to set request user in client test

I am testing a view and my test looks like:
def test_profile(self, user_id):
user = User.objects.create_user(username="myusername", password="password", email="abc#testmail.com")
self.client.user = user
print(user.id)
request = self.client.get("/account/profile/{}/".format(user_id), follow=True)
self.assertEqual(request.status_code, 200)
Here my profile view has a login_required decorator. How can I set user to request.user?
I was trying to do the same myself but found out that Django Test Client does not set the user in the request and it is not possible to set request.user while using Client any other way. I used RequestFactory to that.
def setUp(self):
self.request_factory = RequestFactory()
self.user = User.objects.create_user(
username='javed', email='javed#javed.com', password='my_secret')
def test_my_test_method(self):
payload = {
'question_title_name': 'my first question title',
'question_name': 'my first question',
'question_tag_name': 'first, question'
}
request = self.request_factory.post(reverse('home'), payload)
request.user = self.user
response = home_page(request)
More about request factory here
Try this:
from django.test import TestCase, Client
from django.contrib.auth.models import User
class YourTestCase(TestCase):
def test_profile(self, user_id):
user = User.objects.create(username='testuser')
user.set_password('12345')
user.save()
client = Client()
client.login(username='testuser', password='12345')
response = client.get("/account/profile/{}/".format(user.id), follow=True)
self.assertEqual(response.status_code, 200)
Here, I first create the user and set the login credentials for the user. Then I create a client and login with that user. So in your views.py, when you do request.user, you will get this user.
If you use django.test you can do something like that:
self.client.force_login(user)
This works:
self.client.force_authenticate(user=user)
If you have a response, you can access response.context['user'].
If you need a response object, just call any view that will create a context, e.g. response = self.client.get('/').

login() in Django testing framework

I have started using Django's testing framework, and everything was working fine until I started testing authenticated pages.
For the sake of simplicity, let's say that this is a test:
class SimpleTest(TestCase):
def setUp(self):
user = User.objects.create_user('temporary', 'temporary#gmail.com', 'temporary')
def test_secure_page(self):
c = Client()
print c.login(username='temporary', password='temporary')
response = c.get('/users/secure/', follow=True)
user = User.objects.get(username='temporary')
self.assertEqual(response.context['email'], 'temporary#gmail.com')
After I run this test, it fails, and I see that printing return value of login() returns True, but response.content gets redirected to login page (if login fails authentication decorator redirects to login page). I have put a break point in decorator that does authentication:
def authenticate(user):
if user.is_authenticated():
return True
return False
and it really returns False. Line 4 in test_secure_page() properly retrieves user.
This is the view function:
#user_passes_test(authenticate, login_url='/users/login')
def secure(request):
user = request.user
return render_to_response('secure.html', {'email': user.email})
Of course, if I try to login through application (outside of test), everything works fine.
The problem is that you're not passing RequestContext to your template.
Also, you probably should use the login_required decorator and the client built in the TestCase class.
I'd rewrite it like this:
#views.py
from django.contrib.auth.decorators import login_required
from django.shortcuts import render
from django.contrib.auth import get_user_model
#login_required(login_url='/users/login')
def secure(request):
user = request.user
return render(request, 'secure.html', {'email': user.email})
#tests.py
class SimpleTest(TestCase):
def setUp(self):
User = get_user_model()
user = User.objects.create_user('temporary', 'temporary#gmail.com', 'temporary')
def test_secure_page(self):
User = get_user_model()
self.client.login(username='temporary', password='temporary')
response = self.client.get('/manufacturers/', follow=True)
user = User.objects.get(username='temporary')
self.assertEqual(response.context['email'], 'temporary#gmail.com')
It can often be useful to use a custom auth backend that bypassess any sort of authentication during testing:
from django.contrib.auth import get_user_model
class TestcaseUserBackend(object):
def authenticate(self, testcase_user=None):
return testcase_user
def get_user(self, user_id):
User = get_user_model()
return User.objects.get(pk=user_id)
Then, during tests, add yourapp.auth_backends.TestcaseUserBackend to your AUTHENTICATION_BACKENDS:
AUTHENTICATION_BACKENDS = [
"akindi.testing.auth_backends.TestcaseUserBackend",
]
Then, during tests, you can simply call:
from django.contrib.auth import login
user = User.objects.get(…)
login(testcase_user=user)
Token based authentication:
I was in same situation. I found solution in which actually I did generate a user for login purpose in setUp method. Then later in the test methods, I tried to get the token and passed it along with request data.
setUp:
create a user
self.pravesh = User.objects.create(
email='psj.aaabbb#gmail.com',
first_name='Pravesh',
last_name='aaabbb',
phone='5456165156',
phonecountrycode='91'
)
set password for the user
self.password = 'example password'
self.pravesh.set_password(self.password)
test_method:
create client
client.login(email=self.pravesh.email, password=self.password)
get token (in case of token auth)
token = Token.objects.create(user=self.pravesh)
pass login information
response = client.post(
reverse('account:post-data'),
data = json.dumps(self.data),
HTTP_AUTHORIZATION='Token {}'.format(token),
content_type = 'application/json'
)

Categories