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'})`
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 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)
I am trying to test the admin changelist views if they work properly. This is the code that I use:
from django.test import Client
from django.test import TestCase
class TestAdminViews(TestCase):
def test_admin(self, user = None, name = None):
client=Client()
if user is None:
password = "test"
user = User.objects.create_superuser('testadmin', 'test#test.com', password)
success=client.login(username = user.username, password = password)
self.assertTrue(success, "Login Failed!")
app="messwertdb"
mymodels=["messrun","messung"]
for model in mymodels:
response = client.get(reverse('admin:%s_%s_changelist'% (app, model)))
print reverse('admin:%s_%s_changelist'% (app, model)), response.status_code, response.content.count("exception")#test line self.assertEqual(response.status_code,200,"Response not ok!")
I have broken one of the views by trying to display a nonexisting attribute. So if I try to get it in the browser I get an AttributeError (which results in a 500 response if DEBUG=False). However, in my test I always get a 200 response -meaning no testfailures. If I enter the exact same code in the shell I get the proper Error. In the test case it seems to return an empty but otherwise correct changelist.
The broken admin.py:
class MessRunAdmin(admin.ModelAdmin):
list_display=('type','date','path','failure')
def failure(self, obj):
return obj.nonexisting #this does not exist
It was a simple mistake: I forgot to load the fixture so the testdatabase was empty and as it did not try to show any instance it did not call the failing attribute. And in the shell it was using the deployment database which was not empty.
So I changed it to the following code, which now fails:
class TestAdminViews(TestCase):
fixtures= ["messwertdb.yaml"]
def test_admin(self, user = None, name = None):
.......
I'm developing a REST API which takes POST requests from some really brain-dead software which can't PATCH or anything else. The POSTs are to update Model objects which already exist in the database.
Specifically, I'm POSTing data for objects with a related field (a SlugRelatedField, as the POSTer knows the 'name' attribute but NOT the 'pk'). However, I need to return a 404 if the POSTer sends data where the 'name' returns nothing on the SlugRelatedField (e.g. the related object does not exist). I've been through this with a debugger but it seems that DRF uses some Django signals magic to do it The Way DRF Does It™, which is to return a 400 BAD REQUEST. I don't know how to modify this - only when it's the above condition and not a true 400-worthy POST - into a 404.
By the way, pre_save() in my view is NOT executing during execution of the failing test.
Here's the serializer:
class CharacterizationSerializer(serializers.ModelSerializer):
"""
Work-in-progress for django-rest-framework use. This handles (de)serialization
of data into a Characterization object and vice versa.
See: http://www.django-rest-framework.org/tutorial/1-serialization
"""
creator = serializers.Field(source='owner.user.username')
sample = serializers.SlugRelatedField(slug_field='name',
required=True,
many=False,
read_only=False)
class Meta:
model = Characterization
# leaving 'request' out because it's been decided to deprecate it. (...maybe?)
fields = ('sample', 'date', 'creator', 'comments', 'star_volume', 'solvent_volume',
'solution_center', 'solution_var', 'solution_minimum', 'solution_min_stddev',
'solution_test_len',)
And here's the view where pre_save isn't being run in the given test (but does get run in some others):
class CharacterizationList(generics.ListCreateAPIView):
queryset = Characterization.objects.all()
serializer_class = CharacterizationSerializer
permission_classes = (AnonPostAllowed,) # #todo XXX hack for braindead POSTer
def pre_save(self, obj):
# user isn't sent as part of the serialized representation,
# but is instead a property of the incoming request.
if not self.request.user.is_authenticated():
obj.owner = get_dummy_proxyuser() # this is done for CharacterizationList so unauthed users can POST. #todo XXX hack
else:
obj.owner = ProxyUser.objects.get(pk=self.request.user.pk)
# here, we're fed a string sample name, but we need to look up
# the actual sample model.
# #TODO: Are we failing properly if it doesn't exist? Should
# throw 404, not 400 or 5xx.
# except, this code doesn't seem to be run directly when debugging.
# a 400 is thrown; DRF must be bombing out before pre_save?
obj.sample = Sample.objects.get(name=self.request.DATA['sample'])
And for good measure, here's the failing test:
def test_bad_post_single_missing_sample(self):
url = reverse(self._POST_ONE_VIEW_NAME)
my_sample_postdict = self.dummy_plqy_postdict.copy()
my_sample_postdict["sample"] = "I_DONT_EXIST_LUL"
response = self.rest_client.post(url, my_sample_postdict)
self.assertTrue(response.status_code == 404,
"Expected 404 status code, got %d (%s). Content: %s" % (response.status_code, response.reason_phrase, response.content))
If I put a breakpoint in at the self.rest_client.post() call, the response already has a 400 in it at that point.
You can use a Django Shortcut for that, getting the obj.sample:
from django.shortcuts import get_object_or_404
obj.sample = get_object_or_404(Sample, name=self.request.DATA['sample'])
Instead of using pre_save why not override post in your API view:
def post(self, request, *args, **kwargs):
...other stuff
try:
obj.sample = Sample.objects.get(name=self.request.DATA['sample'])
...or whatever other tests you want to do
except:
return Response(status=status.HTTP_404_NOT_FOUND)
response = super(CharacterizationList, self).post(request, *args, **kwargs)
return response
Make sure you import DRF's status:
from rest_framework import status
Also, note you will likely want to be more specific with the Exceptions you catch. Django's get method will return either DoesNotExist if nothing matches or MultipleObjectsReturned if more than one object matches. The relevant documentation:
Note that there is a difference between using get(), and using
filter() with a slice of [0]. If there are no results that match the
query, get() will raise a DoesNotExist exception. This exception is an
attribute of the model class that the query is being performed on - so
in the code above, if there is no Entry object with a primary key of
1, Django will raise Entry.DoesNotExist.
Similarly, Django will complain if more than one item matches the
get() query. In this case, it will raise MultipleObjectsReturned,
which again is an attribute of the model class itself.
I am trying to use Google App Engine queues API, I am having problems on testing this. It seems that in some part of the process the CSRF it's not working.
as I understand the api executes the task calling the url and making and http request in background.
The complete url is the API is calling is → http://localhost.localdomain:8000/admin/cooking/recipe/36/chefworker/
When it raises this exception:
Traceback (most recent call last):
File "/home/mariocesar/Proyectos/Cooking/cooking/django/core/handlers/base.py", line 100, in get_response
response = callback(request, *callback_args, **callback_kwargs)
File "/home/mariocesar/Proyectos/Cooking/cooking/django/views/decorators/csrf.py", line 24, in wrapped_view
resp.csrf_exempt = True
AttributeError: 'NoneType' object has no attribute 'csrf_exempt'
So, the csrf middleware, the cookie, some data or the response itself is missing from the request that the GAE api makes to execute the task in the background.
How to solve this without disabling CSRF on Django? however, it's posible with djangoappengine at all?
Down are the models.py and admin.py files I am using.
models.py
from django.db import models
class Recipe(models.Model):
name = models.CharField(max_length=140)
description = models.TextField()
cooking_time = models.PositiveIntegerField()
status = models.CharField(max_length=40)
def __unicode__(self):
return self.name
def cookthis(self):
import time
self.status = 'The chef is cooking this recipe'
self.save()
time.sleep(obj.cooking_time)
self.status = 'It\'s done ! the recipe is ready to serve'
self.save()
admin.py
import logging
from django.contrib import admin, messages
from django.http import HttpResponse
from django.utils.functional import update_wrapper
from django.contrib.admin.util import unquote
from django.shortcuts import get_object_or_404, render_to_response
from django import template
from django.core.urlresolvers import reverse
from google.appengine.api import taskqueue
from google.appengine.api.taskqueue import TaskAlreadyExistsError
from cooking.models import Recipe
from django.views.decorators.csrf import csrf_exempt
class AdminRecipe(admin.ModelAdmin):
def get_urls(self):
from django.conf.urls.defaults import patterns, url
def wrap(view):
def wrapper(*args, **kwargs):
return self.admin_site.admin_view(view)(*args, **kwargs)
return update_wrapper(wrapper, view)
info = self.model._meta.app_label, self.model._meta.module_name
urlpatterns = super(AdminRecipe, self).get_urls()
myurls = patterns('',
url(r'^(.+)/cook/$',
wrap(self.cook_view),
name='%s_%s_chefworker' % info),
url(r'^(.+)/chefworker/$',
wrap(self.chefworker_worker),
name='%s_%s_chefworker' % info),
)
return myurls + urlpatterns
def cook_view(self, request, object_id, extra_context=None):
obj = get_object_or_404(Recipe, pk=unquote(object_id))
if request.POST:
try:
taskqueue.add(
name="recipie-%s" % obj.id,
url=reverse('admin:cooking_recipe_chefworker', args=(obj.id,))
)
messages.add_message(request, messages.INFO, 'Chef is cooking the recipe.')
except TaskAlreadyExistsError:
messages.add_message(request, messages.ERROR, 'chef is already cooking that recipe.')
context_instance = template.RequestContext(request, current_app=self.admin_site.name)
return render_to_response("admin/cooking/recipe/cook_view.html", {'object': obj}, context_instance=context_instance)
#TODO: Add csrf token on form
#csrf_exempt
def chefworker_worker(self, request, object_id, extra_context=None):
import time
if request.POST:
obj = get_object_or_404(Recipe, pk=unquote(object_id))
obj.cookthis()
return HttpResponse('done')
admin.site.register(Recipe, AdminRecipe)
IMPORTANT NOTE:
Was hard to debug this error, cause the dev_appserver logger was just raising 403 errors, no other info; so, I have to patch the file google/appengine/api/taskqueue/taskqueue_stub.py line 574 and add "logging.info('response --- \n%s' % result)" to get the output.
If you have the CsrfViewMiddleware enabled, Django will require a csrf_token in all POSTs to your views.
Django provides a decorator, #csrf_exempt, that you should place on your task queue views. This turns off the middleware just for those views.
Alternatively, you can avoid using CsrfViewMiddleware altogether and instead use the #csrf_protect decorator where you need it. I don't recommend doing this -- it's probably safer to protect everywhere and carve out a small number of exemptions for your task queue views.
(One last note: both answers above -- that something is wrong with your view, or that you should just use GET for the task queue -- strike me wrong. There's nothing wrong with your view, and POST is the right verb to use for task queue tasks.)
Looking at the source of csrf.py, it looks like this would only occur if your view function is returning None (or not explicitly returning, in which case Python would return None implicitly). Looking at your code, I don't see how that could occur, though - are you sure this is your exact deployed code?
Also, you probably don't want to use get_object_or_404 inside a task queue task - if it can't find the object, it'll throw a 404, which will cause the task to error and retry indefinitely.
You also shouldn't need CSRF protection (per your TODO); instead, make sure the task queue URL is marked admin-only, and it will only ever be called by the task queue service.
I'm not an expert, but you may try using GET instead of POST. See http://groups.google.com/group/django-non-relational/browse_thread/thread/e6baed5291aed957/d6c42150c8e246e1?lnk=gst&q=queue#d6c42150c8e246e1 (the last entry)