I tried to create a test in my tests.py
class TaskViewTests(TestCase):
def test_task_view_with_no_task(self):
"""
If no task exist, an appropriate message should be displayed.
"""
userName = 'esutek'
response = self.client.get(reverse('actuser:task',args=(userName,)))
self.assertEqual(response.status_code, 200)
self.assertContains(response, "No task are available.")
self.assertQuerysetEqual(response.context['taskList'], [])
However it gives me this error message.
I don't have any clue why this happened. I just followed the tutorial.
actuser:task
views.py
def task(request, userName):
""" User task list in actInbox
"""
user = ActuserViewModel()
user.get_task_list(userName)
return render(request, 'actuser/task.html', {
'userName': userName,
'taskList': user.taskList,
'dateToday': user.dateToday,
})
viewmodels.py
def get_task_list(self, userName):
self.taskList = Task.objects.filter(executor = userName, parent_task_id=EMPTY_UUID).order_by('due_date')
#get date now with this format 05/11
self.dateToday = datetime.date.today()
Actually I got 2 urls...
this is from the project
url(r'^(?P<userName>[0-9a-zA-Z--]+)/', include('actuser.urls', namespace="actuser")),
and this one is from actuser.urls
url(r'^task/$', views.task, name='task'),
HTTP 302 means that you are redirected to some other URL. You can do a redirect intentionally if you use a RedirectView for example, or accidentally if you forget to write slash at the end of the request URL and you have APPEND_SLASH enabled (in that case, you get HTTP 301 instead of 302).
You need a slash at the end:
url(r'^(?P<userName>[0-9a-zA-Z-]+)/task/$', ...
You could be getting a redirect if your view requires login.
You need to login first, this is a good example of how to do it:
Django: test failing on a view with #login_required
Briefly:
class LoginTestCase(TestCase):
def setUp(self):
self.client = Client()
self.user = User.objects.create_user('john', 'lennon#thebeatles.com', 'johnpassword')
def testLogin(self):
self.client.login(username='john', password='johnpassword')
response = self.client.get(reverse('testlogin-view'))
self.assertEqual(response.status_code, 200)
Related
Django v1.10
FormView code:
class PasswordResetConfirmView(FormView):
template_name = "dashboard/account/reset_password_form.html"
success_url = '/dashboard/'
form_class = SetPasswordForm
def authenticate_password_token(self, request, uidb64=None, token=None, encodedtimestring=None):
try:
uid = force_text(urlsafe_base64_decode(uidb64))
user = User.objects.get(pk=uid)
timestring = force_text(urlsafe_base64_decode(encodedtimestring))
timestamp = timeparse(timestring)
timediff = timezone.now() - timestamp
except (TypeError, ValueError, OverflowError, User.DoesNotExist):
user = None
timediff = None
if timediff is None or timediff.days < 0 or timediff.days > PASSWORD_RESET_TIMEOUT_DAYS:
messages.error(request, _(
'The reset password link is no longer valid.'))
return None
if user is None or not default_token_generator.check_token(user, token):
messages.error(request, _('The reset password link is not valid.'))
return None
return user
def get(self, request, uidb64=None, token=None, encodedtimestring=None, *arg, **kwargs):
form = self.form_class()
assert uidb64 is not None and token is not None and encodedtimestring is not None
user = self.authenticate_password_token(
request, uidb64, token, encodedtimestring)
if user is None:
return redirect(reverse('dashboard-login'))
return self.render_to_response(self.get_context_data(form=form))
def post(self, request, uidb64=None, token=None, encodedtimestring=None, *arg, **kwargs):
form = self.form_class(request.POST)
assert uidb64 is not None and token is not None and encodedtimestring is not None
user = self.authenticate_password_token(
request, uidb64, token, encodedtimestring)
if user is None:
return redirect(reverse('dashboard-login'))
if not form.is_valid():
return self.form_invalid(form)
new_password = form.cleaned_data['new_password2']
try:
with transaction.atomic():
user.auth_token.delete()
Token.objects.create(user=user)
user.set_password(new_password)
user.save()
except:
messages.error(request, _('Password reset was unsuccessful.'))
return redirect(reverse('dashboard-login'))
messages.success(request, _('Password has been reset.'))
return redirect(reverse('dashboard-login'))
urls.py:
url(r'^(?i)recover/password/(?P<uidb64>[0-9A-Za-z]+)/(?P<token>.+)/(?P<encodedtimestring>.+)/$',
views.PasswordResetConfirmView.as_view(), name='reset-password-confirm'),
testclass parent:
class BaseApiTest(TestCase):
def setUp(self):
superuser = User.objects.create_superuser(
'test', 'test#api.com', 'testpassword')
self.factory = RequestFactory()
self.user = superuser
self.client.login(username=superuser.username, password='testpassword')
My attempt at writing the test case:
class ResetPasswordEmailTest(BaseApiTest):
def test_password_reset_form(self):
"""
Ensure that the authenticate token works
"""
self.client.logout()
token = default_token_generator.make_token(self.user)
uidb64 = force_bytes(self.user.id)
timenow = force_bytes(timezone.now())
response = self.client.get(
reverse('reset-password-confirm',
args=[urlsafe_base64_encode(uidb64), token,
urlsafe_base64_encode(timenow)]))
self.assertEqual(response.status_code, status.HTTP_200_OK)
Error message that I got:
tests/password_tests.py", line 129, in test_password_reset_form
self.assertEqual(response.status_code, status.HTTP_200_OK)
AssertionError: 302 != 200
Am not sure how to write a test class to test all 3 methods of the formview. My attempt was just to test the get method
UPDATE:
The real reason for the failure has nothing to do with the user login but somehow the check_token method inherent in the PasswordTokenGenerator was failing my tests.
And as I do more research, I think it would be better that I upgrade Django v1.10 to v1.11 where I need to rewrite this whole thing which may end up invalidating the need for this question.
Your get method of PasswordResetConfirmView is responding with 2 different responses.
Redirect
When user is None it is responding with a Redirect URI
if user is None:
return redirect(reverse('dashboard-login'))
So in that case the status code of response will be HTTP 302 means
that you are redirected to some other URL.
Render to response
When there is user, then the information returned with the response is the method used in the request.
return self.render_to_response(self.get_context_data(form=form))
So in that case the status code of response will be HTTP 200 means
the request has succeeded.
Solution:
In order to make your test case pass you can use assertRedirects
def test_password_reset_form(self):
...
# A URL that redirects can be followed to termination.
response = self.client.get(reverse('reset-password-confirm', args=[...]), follow=True)
self.assertRedirects(response, reverse('dashboard-login'), status_code=302, target_status_code=200)
self.assertEqual(len(response.redirect_chain), 2)
If your request used the follow argument, the expected_url and target_status_code will be the url and status code for the final point of the redirect chain.
Reference:
https://github.com/django/django/blob/master/tests/test_client/tests.py
Alternatively you can create two separate test cases (when user is logged-in, when user is not logged-in) and use assertEqual
# Test for logged in user.
def test_password_reset_form_for_logged_in_user(self):
...
# do stuff
self.assertEqual(response.status_code, status.HTTP_200_OK)
# Test if user not logged in.
def test_password_reset_form_if_user_not_logged_in(self):
...
# do stuff
self.assertEqual(response.status_code, status.HTTP_302_FOUND)
Problem (Post Mortem):
Let's start with defining the problem:
Status 302:
The requested resource resides temporarily under a different URI. -source
So that leads us to assume that you are getting redirected thus the test fails.
Where this happens:
In your get() method we find this code:
if user is None:
return redirect(reverse('dashboard-login'))
So if the user is Anonymous (Unauthenticated) the view redirects him to the login page.
From the above, we can state that the problem occurs when you make an unauthenticated request to PasswordResetConfirmView.get().
Solution:
You need to authenticate (login) your user before you make the request.
You can do this by utilizing the force_login() method:
If your site uses Django’s authentication system, you can use the force_login() method to simulate the effect of a user logging into the site. Use this method instead of login() when a test requires a user be logged in and the details of how a user logged in aren’t important.
def my_test_case():
... Do stuff ...
# Login
self.client.force_login(self.user)
# Make the request
response = self.client.get(...)
Hi I am trying to write a test case for my app. URL = '/api/project/'. I have enabled get, post and put methods along with authentication. But still i get 401 error for post request
class EntryResourceTest(ResourceTestCaseMixin, TestCase):
class Meta:
queryset = Project.objects.all()
resource_name = 'project'
allowed_methods = ['get', 'post', 'put']
authentication = Authentication()
authorization = Authorization()
def setUp(self):
super(EntryResourceTest, self).setUp()
# Create a user.
self.username = 'daniel'
self.password = 'pass'
self.user = User.objects.create_user(self.username, 'daniel#example.com', self.password)
def login(self):
return self.api_client.client.login(
username=self.username, password=self.password)
def get_credentials(self):
return self.create_basic(username=self.username, password=self.password)
def test_post_list(self):
self.login()
req_get = self.api_client.get('/api/project/', format='json', authentication=self.get_credentials()) # -> get works and i get 200 status code
req_post = self.api_client.post('/api/project/', format='json', data=self.post_data, authentication=self.get_credentials())
I run the test case using following command , and here get works fine but not the post request. And get request works even if i don't pass authentication parameter as it uses the default login which i have defined in self.login()
django-admin test myapp.api.project.tests
Use BasicAuthentication instead of Authentication.
self.create_basic(...) create a headers for BasicAuthentication.
def create_basic(self, username, password):
"""
Creates & returns the HTTP ``Authorization`` header for use with BASIC
Auth.
"""
I've been trying to implement a user profile system in Django that allows you to simply request "/profile/" as the url to redirect to your user profile if logged in and to the login page if not.
The test is written as follows
class LoggedInProfileTest(TestCase):
def setUp(self):
self.factory = RequestFactory()
self.user = User.objects.create_user(
username='Milhouse',
email="milhouse#gmail.com",
password="MilhousePW")
def tearDown(self):
self.user.delete()
def test_user_profile_redirect(self):
request = self.factory.get("/profile/")
request.user = self.user
response = profile_redirect(request)
self.assertRedirects(
response,
expected_url="/profile/Milhouse/")
The actually functions properly and redirects as desired but the test refuses to pass with the following error thrown.
AttributeError: 'HttpResponseRedirect' object has no attribute 'client'
URL routing code
# main urls.py
urlpatterns = [
...
url(r'^profile/', include('profiles.urls')),
...
]
# profiles.py
urlpatterns = [
url(r'^(?P<username>[\w+-.#]+)/$',
views.user_profile,
name="user_profile"),
url(r'^$',
views.profile_redirect,
name="profile_redirect")
]
Views code
#login_required
def profile_redirect(request):
return redirect("user_profile", request.user.username)
def user_profile(request, username):
pass
All help is appreciated :)
You should have used client provided by Testcase.
First you need to create a user for testing in the database. You should do this explicitly in you test code.
Let's assume that the user you want to login in the test case has a username username and a password password. Then you can do the following:
self.client.login(username=username, password=password) # This do the login work
response = self.client.get('/profile')
# here do your assert work
Hope it helps.
I am learning testing in Django, and have a view which I want to test. This view should only be accessed by staff users. Suppose the view is:
def staff_users(request):
....
# some logic
return HttpResponseRedirect('/repositories/')
if the request is coming from staff users, it should redirect to repositories otherwise I should get something like permission denied. I am starting with something like in tests.py.
def test_request_object(self):
self.user = User.objects.create_user(
username='abc', email='abc#gmail.com', password='1234')
request = HttpRequest()
# User send a request to access repositories
response = staff_users(request)
self.assertIsNone(response)
The problem is here I am not associating my request object with any users, and I also got to know about from django.contrib.admin.views.decorators import staff_member_required but not sure how to use them here. Could anyone tell me how should I test my view should only be accessed by staff users?
All you need to do is decorate your view which you want to protect as shown below:
#staff_member_required
def staff_users(request):
....
# some logic
return HttpResponseRedirect('/repositories/')
If you want a custom logic for testing instead of using django decorator then you can write your own decorator as well.
def staff_users_only(function):
def wrap(request, *args, **kwargs):
profile = request.session['user_profile']
if profile is True: #then its a staff member
return function(request, *args, **kwargs)
else:
return HttpResponseRedirect('/')
wrap.__doc__=function.__doc__
wrap.__name__=function.__name__
return wrap
and use it as:
#staff_users_only
def staff_users(request):
....
# some logic
return HttpResponseRedirect('/repositories/')
Edit
Association of sessions on request object for testing can be done as:
def test_request_object(self):
self.user = User.objects.create_user(
username='abc', email='abc#gmail.com', password='1234')
request = HttpRequest()
#create a session which will hold the user profile that will be used in by our custom decorator
request.session = {} #Session middleware is not available in UnitTest hence create a blank dictionary for testing purpose
request.session['user_profile'] = self.user.is_staff #assuming its django user.
# User send a request to access repositories
response = staff_users(request)
#Check the response type for appropriate action
self.assertIsNone(response)
Edit 2
Also it would be a far better idea to use django Client library for testing:
>>> from django.test import Client
>>> c = Client()
>>> response = c.post('/login/', {'username': 'abc', 'password': '1234'})
>>> response.status_code
200
>>> response = c.get('/user/protected-page/')
>>> response.content
b'<!DOCTYPE html...
I am using Django-registration and I have subclassed the BaseRegistrationView to customised a backend.
However, the HttpResponseRedirect fail to redirect to a new page when the condition is met.
The code is listed below:
class CustomRegistrationView(BaseRegistrationView):
def register(self, request, **cleaned_data):
captchaMatch = False
#check captcha
captchaValue = request.session.get('captcha')
captchaValueSubmitted = cleaned_data['captcha'] + "123"
if captchaValue != captchaValueSubmitted:
return HttpResponseRedirect(reverse('InvitationRequired'))
else:
self.captchaMatch = True
username, email, password = cleaned_data['username'], cleaned_data['email'], cleaned_data['password1']
if Site._meta.installed:
site = Site.objects.get_current()
else:
site = RequestSite(request)
new_user = RegistrationProfile.objects.create_inactive_user(username, email,
password, site)
signals.user_registered.send(sender=self.__class__,
user=new_user,
request=request)
return new_user
def registration_allowed(self, request):
"""
Indicate whether account registration is currently permitted,
based on the value of the setting ``REGISTRATION_OPEN``. This
is determined as follows:
* If ``REGISTRATION_OPEN`` is not specified in settings, or is
set to ``True``, registration is permitted.
* If ``REGISTRATION_OPEN`` is both specified and set to
``False``, registration is not permitted.
"""
return getattr(settings, 'REGISTRATION_OPEN', True)
def get_success_url(self, request, user):
"""
Return the name of the URL to redirect to after successful
user registration.
"""
print "get successful url"
if self.captchaMatch:
return ('registration_complete', (), {})
else:
return HttpResponseRedirect(reverse('InvitationRequired'))
Can anyone help explain why the redirection is executed inside the function register ?
I guess the register method is supposed to return a user or None. Not an HttpRedirect object. Since I'm not familiar with this specific app, try to see how they handle failure on the original method and follow that policy (e.b. return None or raise an Exception).
Also, based on the comments on the get_success_url() method, I would expect the code to be like this:
def get_success_url(self, request, user):
"""
Return the name of the URL to redirect to after successful
user registration.
"""
if self.captchaMatch:
return ('registration_complete', (), {})
else:
return ('InvitationRequired', (), {})
I guess you should not return a HttpResponse instance, as the method is used to get the URL to be redirect to (not the redirection itself).