Testing urls django - django

I have a flatpage in my website. I created it by writing this:
urlpatterns = [path('admin/', admin.site.urls),path('facts/', include('django.contrib.flatpages.urls')),]
After that in admin page I created a page about-me, so the whole url for that page is ‘localhost/facts/about-me/’
I tried to write test for this page:
class StaticURLTests(TestCase):
def setUp(self):
self.guest_client = Client()
def test_about_author(self):
response = self.guest_client.get('/facts/about-me/')
self.assertEqual(response.status_code, 200)
But I get the following error:
self.assertEqual(response.status_code, 200)
AssertionError: 404 != 200
FAILED (failures=1)
Destroying test database for alias ‘default’…
Can’t figure out why. Maybe smb was facing the same problem.
I thought that the error was because of the flatpage but, the same error occured when I tried to test url of the Task model
urlpatterns = [path('task/<slug:slug>/',views.task, name='task'),]
Test:
#classmethod
def setUpClass(cls):
super().setUpClass()
Task = Task.objects.create(
title='Title',
slug=' test-slug',
description='Desc',
)
def setUp(self):
self.guest_client = Client()
user = get_user_model()
self.user = user.objects.create_user(username='Bob')
def test_task_url_exists_at_desired_location_authorized(self):
response = self.guest_client.get('/task/test-slug/')
self.assertEqual(response.status_code, 200)
I tried to change response = self.guest_client.get('/task/test-slug/') to response = self.guest_client.get(reverse('/task/')) but nothing seems to work.

Related

Test View in Django that requires Login session Selenium

I would like to test a View in Django using Sellenium that has a decorator, that requires being logged in:
#method_decorator(login_required, name='dispatch')
This is how mu code looks like:
class TestAddOrder(LiveServerTestCase):
def setUp(self):
self.selenium = webdriver.Firefox()
super(TestAddOrder, self).setUp()
def tearDown(self):
self.selenium.quit()
super(TestAddOrder, self).tearDown()
def test_add_order(self):
selenium = self.selenium
selenium.get('http://127.0.0.1:8000/orders/create/')
date = selenium.find_element_by_name('date').send_keys('01/31/2019')
hours = selenium.find_element_by_id('id_hour').send_keys('18')
submit = selenium.find_element_by_name('submit').send_keys(Keys.RETURN).send_keys(Keys.RETURN)
And the error:
selenium.common.exceptions.NoSuchElementException: Message: Unable to locate element: [name="date"]
How can I keep the session of logged in user when trying to automise my test with Sellenium?
In your setUp() method you should create a user, log in the user and set the session cookie so that it's sent with every subsequent request:
def setUp(self):
self.selenium = webdriver.Firefox()
super().setup()
user = User.objects.create_user(...)
self.client.force_login(user) # TestCase client login method
session_key = self.client.cookies[settings.SESSION_COOKIE_NAME].value
self.selenium.get('http://127.0.0.1/') # load any page
self.selenium.add_cookie({'name': settings.SESSION_COOKIE_NAME, 'value': session_key, 'path': '/'})

django testing class based view

I have a Class based view defined as:
class Myview(LoginRequiredMixin, View):
def post():
#.......
to test this view i tried this
class MyViewTest(TestCase):
def setUp(self):
self.factory = RequestFactory()
self.user = User.objects.create_user(
username='jacob', email='soos#i.com', password='vvggtt')
def view_test(self):
# Create an instance of a POST request.
request = self.factory.post('/my-url/')
request.user = self.user
response = MyView(request)
print (response,"**")
self.assertEqual(response.status_code, 200)
But this gives this error.
response = MyView(request)
TypeError: __init__() takes 1 positional argument but 2 were given
I understand why this error is coming (cinstructor of MyView has 2 ars) but how do i remove it? i couldnt get the details on searching.
we can use django test client
from django.test import Client
class MyViewTest(TestCase):
def setUp(self):
self.client = Client()
self.user = User.objects.create_user(
username='jacob', email='soos#i.com', password='vvggtt')
def view_test(self):
# Create an instance of a POST request.
self.client.login(username="jacob", password="vvggtt")
data = {'name': 'test name'}
res = self.client.post('/my-url/', data)
print(res)
self.assertEqual(res.status_code, 200)
From the docs:
# Use this syntax for class-based views.
response = MyView.as_view()(request)
Try
response = MyView(request=request)
There's a section of the Django docs called Testing Class Based Views which addresses this:
In order to test class-based views outside of the request/response cycle you must ensure that they are configured correctly, by calling setup() after instantiation.
So in your case this looks something like:
def view_test(self):
# Create an instance of a POST request.
request = self.factory.post('/my-url/')
request.user = self.user
my_view = MyView()
my_view.setup(request)
response = my_view.post(request)
self.assertEqual(response.status_code, 200)

Django testing how to assert Redirect

With the folliwing code I get this wrong result : nose.proxy.AssertionError: 302 != 200 : Couldn't retrieve redirection page '/mes_dossiers/': response code was 302 (expected 200)
what is wrong with my code ?
#test.py
from django.test import TestCase, RequestFactory, Client
from ..models import *
from ..views import *
from django.core.management import call_command
class Cas(TestCase):
def setUp(self):
call_command('loaddata', 'fixture_users.json', verbosity=1)
call_command('loaddata', 'xxxxx_tests_xxxx.yaml',
verbosity=1)
def test_dossier_duplicate(self) :
request = self.factory.get('/dossier/3/copier/', follow = True)
request.user = User.objects.get(id=3)
pk = 3
response = dossier_duplicate(request, pk)
response.client = Client()
self.assertRedirects(response,'/mes_dossiers/',status_code=302,
target_status_code=200)
#urls.py
urlpatterns = [
url(r'^dossier/(?P<pk>[0-9]+)/copier/$',views.dossier_duplicate),
]
#views.py
#login_required(login_url="/accounts/login/")
def dossier_duplicate(request, pk):
dossier = get_object_or_404(Dossier, pk=pk)
groupe = dossier.createdBy.groups.all()[0].name
if not in_group(request.user, groupe) :
return HttpResponseForbidden('Vous ne pouvez pas accéder à ce
dossier')
else :
#code to duplicate the "dossier" instance and child contents
#
#
return redirect('/mes_dossiers/')
I've found more examples there:
Django : Testing if the page has redirected to the desired url
https://docs.djangoproject.com/en/3.0/topics/testing/tools/#django.test.SimpleTestCase.assertRedirects
and this worked:
class Cas(TestCase):
def setUp(self):
call_command('loaddata', 'fixture_users.json', verbosity=1)
call_command('loaddata', 'xxx_tests_xxxx.yaml',
verbosity=1)
def test_dossier_duplicate(self) :
request = self.client.get('/dossier/3/copier/', follow = True)
request.user = User.objects.get(id=3)
pk = 3
response = dossier_duplicate(request, pk)
response.client = Client()
response.client.login(username='xxxx', password='xxxxx')
self.assertRedirects(response, '/mes_dossiers/', status_code=302,
target_status_code=200, fetch_redirect_response=True)
I reach this question via google and I have a similar issue with testing redirect.
But my Django is v2.2 and the accepted answer cites a v1.7 which is no longer supported
I then google some more and found this code block at https://docs.djangoproject.com/en/2.2/topics/testing/tools/#django.test.SimpleTestCase.settings
from django.test import TestCase
class LoginTestCase(TestCase):
def test_login(self):
# First check for the default behavior
response = self.client.get('/sekrit/')
self.assertRedirects(response, '/accounts/login/?next=/sekrit/')
I modified for my own use which works.
For the OP case, this is how I believe should work if the OP uses 2.2
def test_dossier_duplicate(self) :
response = self.client.get('/dossier/3/copier/')
self.assertRedirects(response, '/mes_dossiers/')
I am leaving this here. In case future readers have a similar question but have it for Django v2.2
To test redirects you should use the test client instead of RequestFactory.
The API for the RequestFactory is a slightly restricted subset of the
test client API:
It only has access to the HTTP methods get(), post(), put(), delete(),
head(), options(), and trace().
These methods accept all the same
arguments except for follows. Since this is just a factory for
producing requests, it’s up to you to handle the response.
https://docs.djangoproject.com/en/1.11/topics/testing/advanced/#the-request-factory
Try changing self.factory.get to self.client.get
response = self.client.get('/dossier/3/copier/', follow = True)

Django or Django Rest Framework can't resolve url param when testing

I am using Django 1.8.4 and DRF 3.2.1 and when I run requests against the specified urls everything works fine, but when I run the tests with py.test the function update doesn't enter with the task_id param. But the url is fine.
Attached some code from urls.py and views.py and tests.py .. this is an excerpt of the code of course a lot of stuff is missing I just need eyes who can see if I am doing something wrong.
urls.py
from django.conf import settings
from django.conf.urls import include, patterns, url
from rest_framework import routers
from remotetask import views as rt_views
remotetask_detail = rt_views.RemoteTaskViewSet.as_view({'list': 'detail',
'put': 'update'})
remotetask_all = rt_views.RemoteTaskViewSet.as_view({'list': 'list'})
urlpatterns = patterns(
'',
url(r'^remotetasks/$', remotetask_all, name='api-remotetask-all'),
url(r'^remotetasks/(?P<task_id>\d+)/$', remotetask_detail,
name='api-remotetask-detail'),
url(r'^api-auth/', include('rest_framework.urls',
namespace='rest_framework')),
)
views.py
from django.shortcuts import get_object_or_404
from rest_framework import generics
from rest_framework import status
from rest_framework.response import Response
from remotetask.models import RemoteTask
from remotetask.serializers import RemoteTaskSerializer
from rest_framework import viewsets
class RemoteTaskViewSet(viewsets.ViewSet):
queryset = RemoteTask.objects.all()
serializer_class = RemoteTaskSerializer
def detail(self, request, task_id=None):
task = get_object_or_404(RemoteTask, pk=task_id)
serializer = RemoteTaskSerializer(task)
return Response(serializer.data)
def update(self, request, task_id=None):
task = get_object_or_404(RemoteTask, pk=task_id)
new_status = request.data.get('status')
status_changed = task.change_status(new_status, stdout, stderr)
if status_changed:
response_status = status.HTTP_201_CREATED
else:
response_status = status.HTTP_400_BAD_REQUEST
serializer = RemoteTaskSerializer(task)
return Response(serializer.data, status=response_status)
And finally test_views.py
import pytest
from django.core.urlresolvers import reverse
from remotetask.factories import RemoteTaskFactory
from remotetask.models import RemoteTask
from remotetask.views import RemoteTaskViewSet
import json
#pytest.fixture()
#pytest.mark.django_db
def create_remotetask():
remotetask = RemoteTaskFactory.create()
return remotetask
#pytest.fixture()
#pytest.mark.django_db()
def clean_remotetask():
RemoteTask.objects.all().delete()
#pytest.fixture()
def rq_remotetasklist(rf):
url = reverse('api-remotetask-all')
request = rf.get(url)
response = RemoteTaskViewSet.as_view({'list': 'list'})(request)
return response
#pytest.mark.usefixtures('clean_remotetask', 'create_remotetask')
#pytest.mark.django_db
def test_remotetask_changestatus(rq_remotetasklist, rf):
response = rq_remotetasklist
result = response.data.get('results')
id_to_work = result[0]['id']
rt = RemoteTask.objects.get(pk=id_to_work)
assert rt.status == 0
# new request
url = reverse('api-remotetask-detail', kwargs={'task_id':id_to_work})
params = json.dumps({'status': 2, 'stdout': 'test', 'stderr': 'ok'})
request = rf.put(url, data=params,
content_type='application/json')
new_response = RemoteTaskViewSet.as_view({'put': 'update'})(request)
assert new_response.status_code == 200
By default when a new task is created it gets status 0, so I try to change the status to 2 and it fails, doing some debugging I found that is entering on the update function on RemoteTaskViewSet but is not getting the task_id.
I've followed lots of tutorials and changed back and forth the code and still having the same issue, luckily works in production but it worries to me that I cannot make it run test cases from this code.
The error output from py.test is this:
E assert 404 == 200
E + where 404 = <rest_framework.response.Response object at 0x7f9f465ae690>.status_code
I put a debugger into the update function, seems that task_id is None, but when I print request.stream the url is /api/remotetasks/1/ 1 should be the task_id but isn't getting it, I was about to open a ticket on djangoproject but I think isn't a django bug since it works with external client, this must be something on my code, or anything else.
Update: If I use client instead rf and comment the line where I assign new_response with the call of the method, and validate directly the against request.status_code it works!!!.
Something like this:
#pytest.mark.usefixtures('clean_remotetask', 'create_remotetask')
#pytest.mark.django_db
def test_remotetask_changestatus(rq_remotetasklist, client):
response = rq_remotetasklist
result = response.data.get('results')
id_to_work = result[0]['id']
rt = RemoteTask.objects.get(pk=id_to_work)
assert rt.status == 0
# new request
url = reverse('api-remotetask-detail', kwargs={'task_id': id_to_work})
params = json.dumps({'status': 2, 'stdout': 'test', 'stderr': 'ok'})
request = client.put(url, data=params,
content_type='application/json')
assert request.status_code == 201
Now the doubt is why it doesn't work in the previous way?
The issue (as noted in the updated) is in the request assignment:
request = rf.put(url, data=params,
content_type='application/json')
new_response = RemoteTaskViewSet.as_view({'put': 'update'})(request)
assert new_response.status_code == 200
There really is no need to do the custom call to the view. That's already being done by the request assignment. It's not working in the old test because that's not how the view gets called when it's routed through the url routes.
Most importantly in the pervious code the request object is not a request, it's the response to the call. Using the old code I believe this would have worked as well:
#pytest.mark.usefixtures('clean_remotetask', 'create_remotetask')
#pytest.mark.django_db
def test_remotetask_changestatus(rq_remotetasklist, rf):
response = rq_remotetasklist
result = response.data.get('results')
id_to_work = result[0]['id']
rt = RemoteTask.objects.get(pk=id_to_work)
assert rt.status == 0
# new request
url = reverse('api-remotetask-detail', kwargs={'task_id':id_to_work})
params = json.dumps({'status': 2, 'stdout': 'test', 'stderr': 'ok'})
response = rf.put(url, data=params,
content_type='application/json')
assert response.status_code == 200

flask test client post does not create object in database

The following test code does not pass even though manually submitting the form on my web interface actually does work.
import os
from flask.ext.testing import TestCase
from flask import url_for
from config import _basedir
from app import app, db
from app.users.models import User
class TestUser(TestCase):
def create_app(self):
"""
Required method. Always implement this so that app is returned with context.
"""
app.config['TESTING'] = True
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + os.path.join(_basedir, 'test.db')
app.config['WTF_CSRF_ENABLED'] = False # This must be disabled for post to succeed during tests
self.client = app.test_client()
ctx = app.app_context()
ctx.push()
return app
def setUp(self):
db.create_all()
#pass
#app.teardown_appcontext
def tearDown(self):
db.session.remove()
db.drop_all()
#pass
def test_admin_home(self):
# url is the admin home page
url = url_for('admin.index')
resp = self.client.get(url)
self.assertTrue(resp.status_code == 200)
def test_admin_registration(self):
url = url_for('admin.register_view')
data = {'username': 'admin', 'email': 'admin#example.com', 'password': 'admin'}
resp = self.client.post(url, data)
self.assertTrue(resp.status_code == 200)
u = User.query.filter_by(username=u'admin').first()
self.assertTrue(u.username == 'admin') # <----- This fails. Why?
After the test client has post to the register_view url and returns a 200 OK response, I fail to retrieve the 'admin' user from the test database. Why is this so?
Here's the view code (this is a flask admin view)
from flask import request
from flask.ext.admin import expose, AdminIndexView, helpers
from app.auth.forms import LoginForm, RegistrationForm
from app.users.models import User
from app import db
class MyAdminIndexView(AdminIndexView):
#expose('/', methods=('GET', 'POST'))
def index(self):
# handle user login
form = LoginForm(request.form)
self._template_args['form'] = form
return super(MyAdminIndexView, self).index()
#expose('/register/', methods=('GET', 'POST'))
def register_view(self):
# handle user registration
form = RegistrationForm(request.form)
if helpers.validate_form_on_submit(form):
user = User()
form.populate_obj(user)
db.session.add(user)
db.session.commit()
self._template_args['form'] = form
return super(MyAdminIndexView, self).index()
Dumbest mistake ever.
The offending line in my test code is
resp = self.client.post(url, data)
It should be
resp = self.client.post(url, data=data)
I managed to track it down by painstakingly walking through the logic and inserting ipdb.set_trace() step by step until I found the bad POST request made by my client.