I have a django app I wanted to write tests for. For now Im writing integration tests for the urls.
For my signin test , my url looks like:
url(r'^signin/$', login_forbidden(signin), name='signin')
and my test looks like:
from django.test import TestCase
class SigninTest(TestCase):
def test_signin(self):
resp = self.client.get('/signin/')
self.assertEqual(resp.status_code, 200)
However I have no idea to test a a longer url, for instance I have one entry in urls like:
url(
r'^ad_accounts/(?P<ad_account_id>[^/]+)/$',
AdAccountDetailView.as_view(),
name='campaigns'
),
If I repeat the above test I have for the signin page (replacing resp = self.client.get('/ad_accounts/')) returns a failure
======================================================================
FAIL: test_signin (engineoftravel.tests.SigninTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "path/to/project/tests.py", line 7, in test_signin
self.assertEqual(resp.status_code, 200)
AssertionError: 302 != 200
----------------------------------------------------------------------
Ran 1 test in 0.103s
FAILED (failures=1)
why not use reverse: https://docs.djangoproject.com/en/1.10/ref/urlresolvers/#reverse
from django.core.urlresolvers import reverse
....
resp = self.client.get(reverse('campaigns', args=[1]))
where args is the id you need to pass in.
EDIT: since django 1.10 reverse imports from django.urls
from django.test import TestCase
from django.test import Client
from django.core.urlresolvers import reverse
client = Client()
class MainTest(TestCase):
##user login in django
def user_login(self, username, password):
response = self.client.login(username=username, password=username)
return self.assertEqual(resp.status_code, 200)
## first case
def detail(self):
response = self.client.get('/ad_accounts/<test_num>/') ## url regex
self.assertEquals(response.status_code, 200)
def detail2(self):
response = self.client.get(reverse('campaigns'))
self.assertEqual(response.status_code, 200)
For the test failure, you should first login with some test user then make a request, otherwise the page will be redirected to the login page and thus you will get a 302 status code.
Also you can test the status code of the redirected page with client.get('/foo', follow=True) which returns the status code of the sign in page. (In your example)
Related
i deleted the user and change the data = {} part and the issue
still exist and i don't understand what is the problem .
This is the other app tests :
from django.contrib.auth.models import User
from django.urls import resolve, reverse
from django.test import TestCase
from .views import signup
from .forms import SignUpForm
class SignUpTests(TestCase):
def setUp(self):
url = reverse('signup')
self.response = self.client.get(url)
def test_signup_status_code(self):
self.assertEquals(self.response.status_code, 200)
def test_signup_url_resolves_signup_view(self):
view = resolve('/signup/')
self.assertEquals(view.func, signup)
def test_csrf(self):
self.assertContains(self.response, 'csrfmiddlewaretoken')
def test_contains_form(self):
form = self.response.context.get('form')
self.assertIsInstance(form, SignUpForm)
def test_form_inputs(self):
'''
The view must contain five inputs: csrf, username, email,
password1, password2
'''
self.assertContains(self.response, '<input', 5)
self.assertContains(self.response, 'type="text"', 1)
self.assertContains(self.response, 'type="email"', 1)
self.assertContains(self.response, 'type="password"', 2)
class SuccessfulSignUpTests(TestCase):
def setUp(self):
url = reverse('signup')
data = {
'username': 'johndoe',
'email': 'johndoe#gmail.com',
'password1': 'user123',
'password2': 'user123'
}
self.response = self.client.post(url, data)
self.home_url = reverse('home')
def test_redirection(self):
'''
A valid form submission should redirect the user to the home page
'''
self.assertRedirects(self.response, self.home_url)
def test_user_creation(self):
self.assertTrue(User.objects.exists())
def test_user_authentication(self):
'''
Create a new request to an arbitrary page.
The resulting response should now have a `user` to its context,
after a successful sign up.
'''
response = self.client.get(self.home_url)
user = response.context.get('user')
self.assertTrue(user.is_authenticated)
class InvalidSignUpTests(TestCase):
def setUp(self):
url = reverse('signup')
self.response = self.client.post(url, {}) # submit an empty dictionary
def test_signup_status_code(self):
'''
An invalid form submission should return to the same page
'''
self.assertEquals(self.response.status_code, 200)
def test_form_errors(self):
form = self.response.context.get('form')
self.assertTrue(form.errors)
def test_dont_create_user(self):
self.assertFalse(User.objects.exists())
and this is the the results of my tests:
$ python manage.py test
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
........FFF................
======================================================================
FAIL: test_redirection (accounts.tests.SuccessfulSignUpTests)
----------------------------------------------------------------------
Traceback (most recent call last):
File "F:\MyDevelopment\boards-project\myproject\accounts\tests.py", line 55, in
test_redirection
self.assertRedirects(self.response, self.home_url)
File "F:\MyDevelopment\boards-project\venv\lib\site-packages\django\test\testca
ses.py", line 345, in assertRedirects
self.assertEqual(
AssertionError: 200 != 302 : Response didn't redirect as expected: Response code
was 200 (expected 302)
======================================================================
FAIL: test_user_authentication (accounts.tests.SuccessfulSignUpTests)
----------------------------------------------------------------------
Traceback (most recent call last):
File "F:\MyDevelopment\boards-project\myproject\accounts\tests.py", line 68, in
test_user_authentication
self.assertTrue(user.is_authenticated)
AssertionError: False is not true
======================================================================
FAIL: test_user_creation (accounts.tests.SuccessfulSignUpTests)
----------------------------------------------------------------------
Traceback (most recent call last):
File "F:\MyDevelopment\boards-project\myproject\accounts\tests.py", line 58, in
test_user_creation
self.assertTrue(User.objects.exists())
AssertionError: False is not true
----------------------------------------------------------------------
Ran 27 tests in 0.747s
FAILED (failures=3)
Destroying test database for alias 'default'...
and the problem is i applied that on platform for fixing problems like this
and someone copied my files from hithub and you can try to run test on my project and remember the issue in boards/accounts/test.py
and he ran the tests and it fails at the beginning but he said he changed just the data part,
he changed in SuccessfulSignUpTests class the data part to :
data = {
'username': 'johndoe',
'email': 'johndoe#gmail.com',
'password1': 'user123'
'password2': 'user123'
}
and if anyone want to take a look at my repository at Github
this is the link :
my project
I guess the problem is with your view.py
def signup(request):
if request.method == 'POST':
form = SignUpForm(request.POST)
if form.is_valid():
user = form.save()
auth_login(request, user)
return redirect('home')
First of all you should never use url name directly, not good in case you decide to refactor url later on. If however you use, the url should be relative like /home
Best to use reverse. So replace return redirect('home') with
return redirect(reverse('home'))
You will have to add the following import
from django.urls import reverse
I'm trying to do my first tests on Django and I don't know do it or after reading the docs (where it explains a very easy test) I still don't know how do it.
I'm trying to do a test that goes to "login" url and makes the login, and after a succesfull login redirects to the authorized page.
from unittest import TestCase
from django.test.client import Client
class Test(TestCase):
def testLogin(self):
client = Client()
headers = {'X-OpenAM-Username': 'user', 'X-OpenAM-Password': 'password', 'Content-Type': 'application/json'}
data = {}
response = client.post('/login/', headers=headers, data=data, secure=False)
assert(response.status_code == 200)
And the test success, but I don't know if it's beacuse the 200 of loading "/login/" or because the test do the login and after redirect get the 200 code.
How can I check on the test that after the login the url redirected it's the correct? There is a plugin or something that helps with the test? Or where I can find a good tutorial to test my views and the model?
Thanks and regards.
To properly test redirects, use the follow parameter
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.
Then your code is as simple as
from django.test import TestCase
class Test(TestCase):
def test_login(self):
client = Client()
headers = {'X-OpenAM-Username': 'user', 'X-OpenAM-Password': 'password', 'Content-Type': 'application/json'}
data = {}
response = client.post('/login/', headers=headers, data=data, secure=False)
self.assertRedirects(response,'/destination/',302,200)
Note that it's self.assertRedirects rather than assert or assertRedirects
Also note that the above test will most likely fail because you are posting an empty dictionary as the form data. Django form views do not redirect when the form is invalid and an empty form will probably be invalid here.
Django have plenty of tools for testing. For this task, you should use test case class from Django, for example django.test.TestCase.
Then you can use method assertRedirects() and it will check where you've been redirected and with which code. You can find any info you need here.
I've tried to write the code for your task:
from django.test import TestCase
class Test(TestCase):
def test_login(self):
data = {'X-OpenAM-Username': 'user', 'X-OpenAM-Password': 'password'}
response = client.post('/login/', data=data, content_type='application/json', secure=false)
assertRedirects(response, '/expected_url/', 200)
Then you can use python3 manage.py test to run all tests.
I just started learning unittests and stuck with this problem.
I got project structure like this (it’s Django 1.6.2 now):
./manage.py
./myproject
./myproject/urls.py
./myproject/myapp/
./myproject/myapp/urls.py
./myproject/myapp/views.py
./tests/
./test/test_example.py
In the ./myproject/urls.py I have:
from django.conf.urls import patterns, include, url
urlpatterns = patterns('',
url(r'^myapp/', include('myproject.myapp.urls')),
)
In the ./myproject/myapp/urls.py I have:
from django.conf.urls import patterns, url
urlpatterns = patterns('myproject.myapp.views',
url(r'^example1/$', 'itemlist'),
url(r'^example1/(?P<item_id>\w+)/$', 'item'),
)
I wrote basic test and put it into ./test/test_example.py
import unittest
from django.test import Client
class PagesTestCase(unittest.TestCase):
def setUp(self):
self.client = Client()
def test_itemlist(self):
response = self.client.get('/myapp/example1/')
self.assertEqual(response.status_code, 200)
def test_item(self):
response = self.client.get('/myapp/example1/100100/')
self.assertEqual(response.status_code, 200)
I run this tests from shell like this:
cd ./tests
python manage.py test
First test runs OK, but he second always fails with ‘404 not found’ status code.
Both urls are working OK in the browser.
Also, I tried this:
cd ./
python manage.py shell
>>> from django.test.client import Client
>>> c = Client()
>>> r = c.get('/myapp/example1/100100/')
>>> r.status_code
200
I just can’t figure out how to run those tests properly. It seems no pattern that is passed into views as parameter ever works for me. But all fixed urls are found correctly by django.test.client.
Thank you!
EDIT: I just found that 404 fires in my myproject/myapp/views.py
There is a code:
def item(request, item_id):
try:
item = Item.objects.get(pk = int(item_id))
except (ValueError, Item.DoesNotExist):
raise Http404
And here goes the Item.DoesNotExist exception. I have no any idea, why that item not found?
Use the reverse() function instead to build URLs, that is:
In ./myproject/myapp/urls.py file give each URL pattern a name parameter that is for example:
from django.conf.urls import patterns, url
urlpatterns = patterns('myproject.myapp.views',
url(r'^example1/$', 'itemlist', name='example-one'),
url(r'^example1/(?P<item_id>\w+)/$', 'item', name='example-two'),
)
We will use the value given to the name parameter to build URLs.
Then in ./test/test_example.py:
from django.core.urlresolvers import reverse
class PagesTestCase(unittest.TestCase):
...
def test_itemlist(self):
url = reverse('example-one')
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
def test_item(self):
url = reverse('example-two')
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
That should do the trick.
In addition to using reverse, you can get a 404 if the expected model isn't available in the test database (as mentioned in one of the comments). You should also use Django's TestCase instead of python's unittest since the former inherits from the latter, but makes interacting with the database much easier (among other things).
An example that sets up test data:
from django.test import TestCase
from django.urls import reverse
# Or whatever your object is.
from .models import Item
class ItemViewsTestCase(TestCase):
"""Tests for Item views."""
#classmethod
def setUpTestData(cls):
"""Set up test data available for all tests in this class."""
cls.item = Item.objects.create(name='Testing')
def test_item_list_view(self):
# Note, self.client we get for free from Django's TestCase.
response = self.client.get(reverse('itemlist'))
self.assertEqual(response.status_code, 200)
def test_item_detail_view(self):
# This url pattern expects an Item.id so use the item we set up.
url = reverse('item', args=[self.item.id])
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
This is a pretty straight forward test, but I can't seem to get it right.
I want to check which users can login and perform actions (it's part of a larger suite of tests), but the very first step causes some issues.
class SuperUserTest(TestCase):
def setUp(self):
self.client = Client()
self.su = User.objects.create_superuser('super','','the_correct_password')
def test_su_can_login(self):
response = self.client.post(reverse('django.contrib.auth.views.login'),
{'username': 'super', 'password': 'the_wrong_password'})
self.assertEqual(response.status_code,401)
# Success redirects to the homepage, so its 302 not 200
response = self.client.post(reverse('django.contrib.auth.views.login'),
{'username': 'super', 'password': 'the_correct_password'})
self.assertEqual(response.status_code,302)
When I run the test I get:
(my_app)00:20 ~/my_app (master)$ ./manage.py test my_app.SuperUserTest
Creating test database for alias 'default'...
F
======================================================================
FAIL: test_su_can_login (my_app.SuperUserTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "./my_app/tests.py", line 341, in test_su_can_login
self.assertEqual(response.status_code,401)
AssertionError: 200 != 401
----------------------------------------------------------------------
Ran 1 test in 1.180s
FAILED (failures=1)
Destroying test database for alias 'default'...
Why is django returning HTTP code 200 when I incorrectly login?
For additional context, here is how I'm managing the login/logout urls:
urlpatterns = patterns('',
# Examples:
url(r'^accounts/login/?$', 'django.contrib.auth.views.login'),
url(r'^accounts/logout/?$', 'django.contrib.auth.views.logout',
{'next_page': '/'}),
There's some debate in the web community about what the right response to a credentials failure is. For example, here's a Wordpress ticket about switching from 200 to 401. Here on Stack Overflow you can find some recommending 200 and some recommending 401.
Django chooses to return a 200 response along with the re-rendered form.
In my opinion this is the right approach. A 401 or 403 response indicates that the user doesn't have permission to access the resource (URL). But in this case the resource is the login point, and you don't need credentials to access that; by definition it's available to all users. So essentially this case is no different from any other form validation—the server is checking the inputs it was sent, and if they're invalid it returns a 200 response along with the form and an error message.
you can easily customise this behaviour with something like:
from django.contrib.auth.views import LoginView
class LoginView(LoginView):
def form_invalid(self, form):
response = super().form_invalid(form)
response.status_code = 401
return response
and you will need to change the corresponding url:
path("accounts/login/", views.LoginView.as_view(), name="login"),
path("accounts/", include("django.contrib.auth.urls")),
I've just had a look through the Django source code and the reason why is in this function after line 52. If the form is invalid, the login view returns a TemplateResponse, simply returning to the same page and rendering the form with the errors.
I have this view that I want to test
def change_item(request):
name_change = request.POST.get('name_change')
cost_change = request.POST.get('cost_change')
category_change = request.POST.get('category_change')
product_id = request.POST.get('product_id')
category_name = Category.objects.get(name = category_change)
product = Product.objects.get(id__exact = product_id)
product.name = name_change
product.category = category_name
product.price = cost_change
product.save()
return HttpResponse()
And I wrote a test for It,but it doesn't work (I want to test request)
from django.test import RequestFactory
from django.test import TestCase
from views import change_item
from models import Product
from django.utils import unittest
class TestChange_item(TestCase):
def setUp(self):
self.factory = RequestFactory()
def change_item_test(self):
# Create an instance of a GET request.
request = self.factory.get('main/views')
# Test my_view() as if it were deployed at /customer/details
response = change_item(request)
self.assertEqual(response.status_code, 200)
When I run it I get this in console
ERROR: change_item_test (src.main.tests.TestChange_item)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/home/volodia/work/shopping-helper/src/main/tests.py", line 19, in change_item_test
response = change_item(request)
File "/usr/local/lib/python2.7/dist-packages/django/contrib/auth/decorators.py", line 24, in _wrapped_view
if test_func(request.user):
AttributeError: 'WSGIRequest' object has no attribute 'user'
----------------------------------------------------------------------
Ran 1 test in 0.002s
FAILED (errors=1)
What can I do with it ? Or there is some different way of testing requests ?
If you look at the documentation you will see that you need to manually add a user to the request. It doesn't do this for you automatically.
def change_item_test(self):
request = self.factory.get('main/views')
request.user = User.objects.create_user(username="bobalong")
Adding a user like this simulates making a logged in request. If this is NOT what you want then you should create an AnonymousUser instead. In general the error you were getting should provide you with everything you need to know. It is explicitly telling you that the test is failing because the request doesn't have a user. Adding a real user or adding a mock user object to the request is the answer.
Also, you seem to be testing a view that will only work with a POST request. It's going to fail because request.POST does not have the correct dictionary keys. You can create a POST request by doing
factory.post('main/views', {'name_change': 'some name'})`