I wrote Django app and now I'm trying to cover it with automated tests. For testing get_queryset function within my ListView I created a test user and his post, but my test fails with "No User matches the given query". When I execute py manage.py runserver everything is fine, no exceptions are raised and the page's displayed properly. I'm new to Django testing so I absolutely have no idea what's going on. Could you help me please?
This is my view from view.py
class UserPostListView(ListView):
"""Displaying a page with a certain user's posts"""
model = Post
template_name = 'blog/user_posts.html'
context_object_name = 'posts'
paginate_by = 5
def get_queryset(self):
"""Dynamic filtering to get posts by a chosen user"""
queryset = super().get_queryset()
user = get_object_or_404(User, username=self.kwargs.get('username'))
return queryset.filter(author=user).order_by('-date_posted')
Test for that view:
class TestUserPostListView(TestCase):
"""Test UserPostListView"""
def setUp(self):
"""Creating a test user and his post to see if the certain
user's page with posts is displayed properly"""
self.factory = RequestFactory()
self.user = User.objects.create_user(
username='test_user',
email='testuser#example.com',
password='fhhewo87539275'
)
self.post = Post.objects.create(
title='test_post',
content='blabla',
author=self.user
)
def test_get_queryset(self):
"""Testing get_queryset function"""
url = reverse('user-posts', kwargs={'username': self.user.username})
request = self.factory.get(url)
view = UserPostListView()
view.setup(request)
queryset = view.get_queryset()
self.assertIn(self.post, queryset)
Traceback:
Traceback (most recent call last):
File "C:\Users\473491\Documents\django\web_blog\env\lib\site-packages\django\shortcuts.py", line 76, in get_object_or_404
return queryset.get(*args, **kwargs)
File "C:\Users\473491\Documents\django\web_blog\env\lib\site-packages\django\db\models\query.py", line 435, in get
raise self.model.DoesNotExist(django.contrib.auth.models.User.DoesNotExist: User matching query does not exist.
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "C:\Users\473491\Documents\django\web_blog\web_blog\apps\blog\test\test_views.py", line 67, in test_get_queryset
queryset = view.get_queryset()
File "C:\Users\473491\Documents\django\web_blog\web_blog\apps\blog\views.py", line 45, in get_queryset
user = get_object_or_404(User, username=self.kwargs.get('username'))
File "C:\Users\473491\Documents\django\web_blog\env\lib\site-packages\django\shortcuts.py", line 78, in get_object_or_404
raise Http404('No %s matches the given query.' % queryset.model._meta.object_name)
django.http.response.Http404: No User matches the given query.
I have found out where the problem was. My UserPostListView is called by the url containing url-variable.
My urlpattern corresponding to the view:
path('users/<str:username>', UserPostListView.as_view(), name='user-posts')
I also use username from the url to filter out posts created by the user with that username by calling get_queryset function in UserPostListView.
I read the docs attentively and found:
When the view is called during the request/response cycle, the setup() method assigns the HttpRequest to the view’s request attribute, and any positional and/or keyword arguments captured from the URL pattern to the args and kwargs attributes, respectively.
It means that instead of view.setup(request) I had to write view.setup(request, username=self.user.username)
When I fixed it the test started running properly and didn't fail.
Related
I'm trying to get a list of Scripts
I'm not sure why this error is django throwing however, Can anybody identify if there's something is wrong.
Model
class Script(models.Model):
name = models.CharField(max_length=100)
script = models.TextField()
def __str__(self):
return self.name
Serializer
class ScriptSerializer(serializers.Serializer):
id = serializers.IntegerField(read_only=True)
name = serializers.CharField(read_only=True)
script = serializers.CharField(read_only=True)
view;
#api_view(['GET'])
def scripts(request):
scripts = Script.objects.all()
serializer = ScriptSerializer(scripts, many=True)
return Response(serializer.data)
but when I call this view, I get this error
Internal Server Error: /api/v1/seo/scripts
Traceback (most recent call last):
File "E:\folder\venv\lib\site-packages\django\core\handlers\exception.py", line 55, in inner
response = get_response(request)
File "E:\folder\venv\lib\site-packages\django\utils\deprecation.py", line 136, in __call__
response = self.process_response(request, response)
File "E:\folder\venv\lib\site-packages\django\middleware\clickjacking.py", line 27, in process_response
if response.get("X-Frame-Options") is not None:
AttributeError: 'Script' object has no attribute 'get'
I have also seen answers related to that issue in Stackoverflow but not able to reproduce the solution. And also this approach is not new to me, I have been using this type of functions all over the project. But I'm surprised now why it's happening.
Edit:
Imports
from rest_framework.decorators import api_view
from rest_framework.response import Response
from .serializers import ScriptSerializer
from .models import Script
urls
from django.urls import path
from . import views as views
app_name = "seo"
urlpatterns = [
path('scripts', views.Script, name='scripts'),
]
I believe the issue here is in naming collision. Take a closer look to this:
#api_view(['GET'])
def scripts(request):
scripts = Script.objects.all()
serializer = ScriptSerializer(scripts, many=True)
The function named scripts and then this name reassigned with scripts = Script.objects.all(). This may lead to really unexpected behaviours
So try to rename your local variable. I.e.:
#api_view(['GET'])
def scripts(request):
script_items = Script.objects.all()
UPD after urls.py added:
It should point to the function view i.o. model:
path('scripts', views.scripts, name='scripts'),
I am trying to use graphQL queries in django. Basically I have two apps, my 'api' app which contains everything I need to make the queries and another one called 'frontend' from which I call the api to use these queries.
I can use the GraphQL view to type queries in it and it works perfectly, but whenever I try to make the query, I get this: "OrderedDict([('users', None)])"
Result of my query in the GraphQl view
The code:
In 'api' my schema.py:
import graphene
import graphql_jwt
from graphene import relay, ObjectType, AbstractType, List, String, Field,InputObjectType
from graphene_django import DjangoObjectType
from graphene_django.filter import DjangoFilterConnectionField
from datetime import date, datetime
from django.contrib.auth.models import User
from django.contrib.auth import get_user_model
....
class Query(graphene.ObjectType):
me = graphene.Field(UserType)
users = graphene.List(UserType)
profile = relay.Node.Field(ProfileNode)
all_profiles = DjangoFilterConnectionField(ProfileNode)
def resolve_users(self, info):
### Returns all users ###
user = info.context.user
if user.is_anonymous:
raise Exception('Not logged!')
if not user.is_superuser:
raise Exception('premission denied')
return User.objects.all()
def resolve_me(self, info):
### Returns logged user ###
user = info.context.user
if user.is_anonymous:
raise Exception('Not logged!')
return user
def resolve_all_profiles(self, info, **kwargs):
### Returns all profiles ###
return Profile.objects.all()
.....
def execute(my_query):
schema = graphene.Schema(query=Query)
return schema.execute(my_query)
And the views.py that calls the app 'api' in my app frontend:
from django.shortcuts import render
import graphene
from api import schema
from django.contrib.auth import authenticate
def accueil(request):
if request.user.is_authenticated:
check = "I am logged"
else:
check = "I am not logged"
result = schema.execute("""query {
users {
id
username
}
}""")
return render(request, 'frontend/accueil.html', {'result' : result.data, 'check' : check})
The template :
<h1>OTC</h1>
<p> the users are : {{result}}</p>
<br/>
<p>{{check}}</p>
login
logout
and finally:
The web page result
and the error in the console:
An error occurred while resolving field Query.users
Traceback (most recent call last):
File "/home/victor/myenv/lib/python3.5/site-packages/graphql/execution/executor.py", line 311, in resolve_or_error
return executor.execute(resolve_fn, source, info, **args)
File "/home/victor/myenv/lib/python3.5/site-packages/graphql/execution/executors/sync.py", line 7, in execute
return fn(*args, **kwargs)
File "/home/victor/poc2/poc2/api/schema.py", line 67, in resolve_users
user = info.context.user
AttributeError: 'NoneType' object has no attribute 'user'
Traceback (most recent call last):
File "/home/victor/myenv/lib/python3.5/site-packages/graphql/execution/executor.py", line 330, in complete_value_catching_error
exe_context, return_type, field_asts, info, result)
File "/home/victor/myenv/lib/python3.5/site-packages/graphql/execution/executor.py", line 383, in complete_value
raise GraphQLLocatedError(field_asts, original_error=result)
graphql.error.located_error.GraphQLLocatedError: 'NoneType' object has no attribute 'user'
Unless you're writing a test client, you probably should not be calling schema.execute from inside a Django view. But assuming that you have your reasons for doing this, your specific problem is that you're not passing the user when you invoke schema.execute in accueil view.
Have a look at the execute documentation and you'll see that you'll need to supply an optional argument for the context. Your code is not supplying a context, hence the info.context is None, as per your exception. Unfortunately, the example
result = schema.execute('{ name }', context_value={'name': 'Syrus'})
is not Django-specific. But I think what works in a Django functional view is:
result = schema.execute(query, context_value=request)
I'm writing a test for a Django Rest Framework view following closely the testing documentation
Here's my simple test:
def test_patient_detail_api_opens(self):
factory = APIRequestFactory()
view =PatientDetailApi.as_view()
request = factory.get(reverse('api_pacjent', kwargs={'pk' :1}))
force_authenticate(request, user=self.user)
response = view(request)
self.assertEqual(response.status_code, 200)
This test fails with the following message:
AssertionError: Expected view PatientDetailApi to be called with a URL keyword argument named "pk". Fix your URL conf, or set the `.lookup_field` attribute on the view correctly.
I fail to understand why this is happening and how to fix this.
The pk kwargs is there in the URL,
according to the docs there's no need to add the lookup-field value explicitly if it defaults to pk,
the view opens correctly and yet this test fails...
Can somebody please explain why this error occurs?
Here's the relevant code:
the 'main' url.py:
urlpatterns = [
url(r'^pacjent/', include('pacjent.urls')),
]
pacjent.urls looks like this:
url(r'^api/szczegoly/(?P<pk>\d+)/$', PatientDetailApi.as_view(), name="api_pacjent"),
And PatientDetailApi is this:
class PatientDetailApi(generics.RetrieveUpdateAPIView):
model = Patient
serializer_class = PatientDetailsSerializer
queryset = Patient.objects.all()
authentication_classes = (SessionAuthentication, BasicAuthentication)
permission_classes = (IsAuthenticated,)
View functions are called with the request and the arguments from the URL. So pass them:
response = view(request, pk=1)
I encountered similar error when I made a mistake of using get_object method in perform_create. Read why this wrong from documentation
perform_create(self,instance):
instance = self.get_object()
So I'm writing a test to check for the occurrence of Validation Errors in my Form.
In doing so I'm getting the following error:
======================================================================
FAIL: test_start_date_before_end_date_errors (reports.tests.ScheduleValidation)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/home/jwe/piesup2/reports/tests.py", line 61, in test_start_date_before_end_date_errors
self.assertFormError(response, 'form', None, 'End Date Must Be Greater Than Start Date')
File "/home/jwe/piesup2/venv/lib/python3.4/site-packages/django/test/testcases.py", line 467, in assertFormError
" response" % form)
AssertionError: The form 'form' was not used to render the response
----------------------------------------------------------------------
The validation error I'm specifically checking for is raised in models.py inside the clean() method
class Schedule(models.Model)
...
...
def clean(self):
if self.start_date and self.end_date:
# Ensure that the end_date occurs after the start_date
if self.end_date <= self.start_date:
err = "End Date Must Be Greater Than Start Date"
raise ValidationError(err)
The Generic CreateView I am testing looks as follows:
class ScheduleCreate(SuccessMessageMixin, FetchURLMixin, CreateView):
model = Schedule
form_class = ScheduleCreateForm
template_name_suffix = '_create_form'
I'm able to render the errors in the view's template as non_field_errors as so:
{% for error in form.non_field_errors %}
<label>{{ error }}</label>
{% endfor %}
My test looks as follows:
class ScheduleValidation(RequiresLogin, TestCase):
def setUp(self):
self._client = client_fixture.create()[0]
# Setup some example sample data for a Schedule
self.data = {
'client': self._client.id,
'schedule_date': today,
'billing_period': 'Q',
'schedule_type': 'SE',
}
def test_start_date_before_end_date_errors(self):
self.data['start_date'] = today
self.data['end_date'] = yesterday
response = self.client.post('reports:schedule-create', self.data, follow=True)
# Check if redirects back to the page
with self.assertTemplateUsed('schedule_create_form.html'):
self.assertFormError(response, 'form', None, 'End Date Must Be Greater Than Start Date')
I'm setting the field type of None in order to access non_field_errors as detailed by the documentation here
What I've Tried Already
Inside my template for the view, I'm able to reference the form using {{ form }}, including any non_field_errors. Which means it is being passed to the template properly.
I can check the context data inside the view itself.
def get_context_data(self, **kwargs):
context = super(ScheduleCreate, self).get_context_data(**kwargs)
assert False
return context
From the breakpoint here I'm able to log the contents of the context variable with Werkzeug in the browser, which shows that 'form' is actually passed to the template
[console ready]
>>> context
{'form': <ScheduleCreateForm bound=True, valid=False, fields=(client;schedule_date;start_date;end_date;billing_period;schedule_type)>, 'view': <reports.views.ScheduleCreate object at 0x7f256eb6c908>}
Which begs the question why am I getting this error, and how would I fix this?
When you use client.post(), you should use the actual URL, not the name of the url.
You could either hardcode it, for example:
response = self.client.post('/reports/schedules/create/, self.data, follow=True)
Or you could reverse the url:
from django.core.urlresolvers import reverse
url = reverse('reports:schedule-create')
response = self.client.post(url, self.data, follow=True)
I'm testing a django app from this tutorial: http://tutorial.djangogirls.org/en/django_admin/README.html
I've created a test:
from django.test import TestCase
from django.utils import timezone
from .models import Post
from django.contrib.auth.models import User
# Create your tests here.
class PostTest(TestCase):
def test_create_post(self):
# Create the post
post = Post()
# Set the attributes
post.author = User
post.title = 'My first post'
post.text = 'This is my first blog post'
post.published_date = timezone.now()
post.created_date = timezone.now()
# Save it
post.save()
# Check we can find it
all_posts = Post.objects.all()
self.assertEquals(len(all_posts), 1)
only_post = all_posts[0]
self.assertEquals(only_post, post)
# Check attributes
self.assertEquals(only_post.author, User)
self.assertEquals(only_post.title, 'My first post')
self.assertEquals(only_post.text, 'This is my first blog post')
self.assertEquals(only_post.published_date.day, post.published_date.day)
self.assertEquals(only_post.published_date.month, post.published_date.month)
self.assertEquals(only_post.published_date.year, post.published_date.year)
self.assertEquals(only_post.published_date.hour, post.published_date.hour)
self.assertEquals(only_post.published_date.minute, post.published_date.minute)
self.assertEquals(only_post.published_date.second, post.published_date.second)
self.assertEquals(only_post.created_date.day, post.created_date.day)
self.assertEquals(only_post.created_date.month, post.created_date.month)
self.assertEquals(only_post.created_date.year, post.created_date.year)
self.assertEquals(only_post.created_date.hour, post.created_date.hour)
self.assertEquals(only_post.created_date.minute, post.created_date.minute)
self.assertEquals(only_post.created_date.second, post.created_date.second)
When I run python manage.py test I get this error:
Creating test database for alias 'default'...
ERROR: test_create_post (blog.tests.PostTest)
Traceback (most recent call last):
File "C:\Users\shenk\Documents\Programming\django_projects\djangogirls\blog\tests.py" , line 13, in test_create_post
post.author = User
File "c:\Users\shenk\Documents\Programming\django_projects\djangogirls\myvenv\lib\site-packages\django\db\models\fields\related.py", line 627, in __set__
self.field.rel.to._meta.object_name,
ValueError: Cannot assign "<class 'django.contrib.auth.models.User'>": "Post.author" must be a "User" instance.
----------------------------------------------------------------------
Ran 1 test in 0.001s
How can I create an object of User instance to test the Post? In my model it's defined as author = models.ForeignKey('auth.User')
This line looks bogus:
# Set the attributes
post.author = User
post.author is expecting for you to assign an instance of the User class to it, not the User class itself. Try something like:
u = User(...)
u.save()
post.author = u