I'm at a bit of a loss as to why two of my tests are conflicting. The patch in one test seems to be persisting to the other test. If I comment the patching test out, or just run the other test by itself there is no problem. My tests are:
class TestUrlRouting(TestCase):
#patch('myapp.views.Home')
def test_home_url_routes_to_home_view(self, mock_home_view):
url = reverse('home')
match = resolve(url)
assert match.func == mock_home_view.as_view()
class TestHomeView(TestCase):
def test_front_page_renders_home_template(self):
response = self.client.get('/')
self.assertTemplateUsed(response, 'home.html')
With the Home view being a TemplateView. The error I end up with is:
Traceback (most recent call last):
File "/path/to/code/source/myapp/tests/test_views.py", line 11, in test_front_page_renders_home_template
response = self.client.get('/')
File "/path/to/code/virtualenv/lib/python3.5/site-packages/django/test/client.py", line 503, in get
**extra)
File "/path/to/code/virtualenv/lib/python3.5/site-packages/django/test/client.py", line 304, in get
return self.generic('GET', path, secure=secure, **r)
File "/path/to/code/virtualenv/lib/python3.5/site-packages/django/test/client.py", line 380, in generic
return self.request(**r)
File "/path/to/code/virtualenv/lib/python3.5/site-packages/django/test/client.py", line 467, in request
six.reraise(*exc_info)
File "/path/to/code/virtualenv/lib/python3.5/site-packages/django/utils/six.py", line 686, in reraise
raise value
File "/path/to/code/virtualenv/lib/python3.5/site-packages/django/core/handlers/base.py", line 242, in get_response
response = self.apply_response_fixes(request, response)
File "/path/to/code/virtualenv/lib/python3.5/site-packages/django/core/handlers/base.py", line 305, in apply_response_fixes
response = func(request, response)
File "/path/to/code/virtualenv/lib/python3.5/site-packages/django/http/utils.py", line 17, in conditional_content_removal
if 100 <= response.status_code < 200 or response.status_code in (204, 304):
TypeError: unorderable types: int() <= MagicMock()
Because I'm not doing anything special with the patch decorator, my guess is that it has something to do with how Django is setting things up. In particular, I found that patching myapp.urls.Home does not work. Instead I need to patch myapp.views.Home, despite the fact that the line under test is in urls.py as url(r'^$', Home.as_view(), name='home'),. Any thoughts as to why this patch is persisting and how I can fix it? Thank you much!
Update:
The problem persists even when mocking myapp.views.Home.as_view instead of just myapp.views.Home (once again, note views is the option that correctly mocks and not urls for some reason).
The call to .as_view() is made only once at import time (first time Django needs to visit "urls.py"), that's why it's "persisted".
IMO your first test case is just testing the Django framework and not anything in your own code except that the home view has a url name of "home". It isn't worth it to be that picky. You can easily cover that reverse('home') gets you to the right view in a another test case where you don't need to mock Django globals.
Related
I have written a flask server which in some cases redirects the user to external sites. I wrote some unit tests using python unittest module. For some of them which are testing the redirect part, I get werkzeug.routing.BuildError. Here is the code for one of the test cases:
with app.app_context(), app.test_request_context():
response = self.app.get('/{0}'.format(test_url), follow_redirects=False)
self.assertEqual(response.status_code, 302)
self.assertEqual(response.location, url_for('https://www.linkedin.com/in/zeinab-abbasimazar-0327aa47', _external=True, _scheme='https'))
And this is the full stack trace:
Ran 1 test in 3.211s
FAILED (errors=1)
Error
Traceback (most recent call last):
File "/usr/lib/python3.6/unittest/case.py", line 59, in testPartExecutor
yield
File "/usr/lib/python3.6/unittest/case.py", line 605, in run
testMethod()
File "/home/zeinab/PycharmProjects/url_shortener/tests.py", line 167, in test_get_long_existing_url
self.assertEqual(response.location, url_for(long_url, _external=True, _scheme='https'))
File "/home/zeinab/.local/lib/python3.6/site-packages/flask/helpers.py", line 370, in url_for
return appctx.app.handle_url_build_error(error, endpoint, values)
File "/home/zeinab/.local/lib/python3.6/site-packages/flask/app.py", line 2215, in handle_url_build_error
reraise(exc_type, exc_value, tb)
File "/home/zeinab/.local/lib/python3.6/site-packages/flask/_compat.py", line 39, in reraise
raise value
File "/home/zeinab/.local/lib/python3.6/site-packages/flask/helpers.py", line 358, in url_for
endpoint, values, method=method, force_external=external
File "/home/zeinab/.local/lib/python3.6/site-packages/werkzeug/routing.py", line 2020, in build
raise BuildError(endpoint, values, method, self)
werkzeug.routing.BuildError: Could not build url for endpoint 'https://www.linkedin.com/in/zeinab-abbasimazar-0327aa47'. Did you mean 'static' instead?
Assertion failed
I have also following line in the setUp method:
app.config['PREFERRED_URL_SCHEME'] = 'https'
I tried patching url_for method as described in this question; but it didn't change my result.
I also tried the _force_https method explained here and saw no change.
I printed out the app.config['wsgi.url_scheme'] when I read this page and it was https.
I am using python 3.6 on an Ubuntu system. Can anyone help me fix this error?
Use directly string with url without url_for()
self.assertEqual(response.location, 'https://www.linkedin.com/in/zeinab-abbasimazar-0327aa47')
Normally url_for() also creates string with url but it do it only for function names in your code - url_for(function_name) - not for urls.
I am now writing tests for my web application in Django. I have an URL 127.0.0.1/home/device/(?P<item>[^/]+). I was testing an invalid URL path.
Here item is a device name in the database. Consider an invalid device and while testing I have given the following code:
response=self.client.get("/health/errorfiles/",{'item':'testdevice_R'})
This give me a 404 response. The same thing I have tried with:
response=self.client.get("/health/errorfiles/testdevice_R/")
But this time, the test runner executes my view function and gives a TypeError since it is not an invalid device.
In both of the methods, which one is correct usage for a get request?
views.py
def showfiles(request,item):
if request.user.is_anonymous(): ## check if the user is valid
return HttpResponseRedirect("/")
db = MySQLdb.connect(host='localhost',user=root,passwd='123mcvis',db='test_db')
s = db.cursor()
username=request.user
s.execute("Select tzone,type from device where user='%s' and device='%s'"%(username,item)
tz=s.fetchone()
timezone.activate(tz[0])
s.close()
return render(request,"Home/showdevices.html")
This is my view function and since the device is invalid, it shows typ error.
class showfile_test(TestCase):
def test_invalid(self):
response=self.client.get("/health/errorfiles/testdevice_R/")
self.assertEqual(response.status_code,404)
Traceback
ERROR: test_invalid (HealthApp.tests2.showfile_test)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/home/vishnumc/vishnu/project/django/official/version6.2.3/HealthApp/tests2.py", line 162, in test_showfiles_with_invalid_deviceid
response=self.client.get("/health/errorfiles/invaliddevice/")
File "/home/vishnumc/vishnu/project/env/local/lib/python2.7/site-packages/django/test/client.py", line 529, in get
**extra)
File "/home/vishnumc/vishnu/project/env/local/lib/python2.7/site-packages/django/test/client.py", line 333, in get
return self.generic('GET', path, secure=secure, **r)
File "/home/vishnumc/vishnu/project/env/local/lib/python2.7/site-packages/django/test/client.py", line 409, in generic
return self.request(**r)
File "/home/vishnumc/vishnu/project/env/local/lib/python2.7/site-packages/django/test/client.py", line 494, in request
six.reraise(*exc_info)
File "/home/vishnumc/vishnu/project/env/local/lib/python2.7/site-packages/django/core/handlers/exception.py", line 39, in inner
response = get_response(request)
File "/home/vishnumc/vishnu/project/env/local/lib/python2.7/site-packages/django/core/handlers/base.py", line 249, in _legacy_get_response
response = self._get_response(request)
File "/home/vishnumc/vishnu/project/env/local/lib/python2.7/site-packages/django/core/handlers/base.py", line 187, in _get_response
response = self.process_exception_by_middleware(e, request)
File "/home/vishnumc/vishnu/project/env/local/lib/python2.7/site-packages/django/core/handlers/base.py", line 185, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/home/vishnumc/vishnu/project/django/official/version6.2.3/HealthApp/views.py", line 118, in showfiles
timezone.activate(tz[0]) ##setting current timezone to user's time zone for display timestamps
TypeError: 'NoneType' object has no attribute '__getitem__'
The first example is a GET request to /health/errorfiles/?item=testdevice_R. In the view, you would then find item in request.GET.
The second example is a GET request to /health/errorfiles/testdevice_R/. There is no data in request.GET and Django will pass item to your view since you have a named group (?P<item>[^/]+) in your URL pattern.
You should use the second version, because you want to test the URL pattern r'/home/device/(?P<item>[^/]+)'.
The second version of your test has uncovered problems in your view. You need to fix the view so that it doesn't raise TypeError.
Ideally, you shouldn't be writing raw SQL like that. Django allows you to do something like the following:
from .models import Device
from django.shortcuts import get_object_or_404, render
def showhome(request, item):
device = get_object_or_404(Device, user=request.user, item=item)
if device is not None:
timezone.activate(device.tzone)
return render(request,"Home/showdevices.html", {'device': device})
If you must use raw SQL then don't use string substitution %(username,item). Your current code exposes you to an SQL injection account. You should change it to:
s.execute("Select tzone,type from device where user=%s" and device=%s, (username, item))
Your code then has handle the case where tz is None, to avoid the TypeError.
tz = s.fetchone()
if tz is not None:
timezone.activate(tz[0])
else:
# Decide what to do if no items returned
I'm trying to run some tests for my Django-Rest-Framework API but am stuck on an error. When I run the following tests, I get the following errors.
Traceback (most recent call last):
File "C:\Users\Bill\SD\DjangoApps\vidapp\startapp\tests.py", line 21, in test_get_user
response = self.client.get('/user/1/')
File "C:\Anaconda\lib\site-packages\django\test\client.py", line 473, in get
response = super(Client, self).get(path, data=data, **extra)
File "C:\Anaconda\lib\site-packages\django\test\client.py", line 280, in get
return self.request(**r)
File "C:\Anaconda\lib\site-packages\rest_framework\test.py", line 143, in request
return super(APIClient, self).request(**kwargs)
File "C:\Anaconda\lib\site-packages\rest_framework\test.py", line 95, in request
request = super(APIRequestFactory, self).request(**kwargs)
File "C:\Anaconda\lib\site-packages\django\test\client.py", line 444, in request
six.reraise(*exc_info)
File "C:\Anaconda\lib\site-packages\django\core\handlers\base.py", line 114, in get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
TypeError: __init__() takes exactly 1 argument (2 given)
Test Cases:
class UserTestCase(APITestCase):
def setUp(self):
helper.reset_test_db()
def test_get_user(self):
response = self.client.get('/user/1/')
print response.content
self.assertEqual(response.data, {'fname':'Generic','lname':'Name','token':'token1'})
URL Config:
url(r'^user/new/$', 'startapp.views.new_user'),
url(r'^user/1/$', 'startapp.views.get_1'),
Views:
class get_1(APIView):
def get(self, request):
user = db_models.User.objects.get(pk=1)
if(user is not None):
serial_user = serial.UserSerializer(user)
return Response(serial_user.data)
else:
return Response(status.HTTP_404_NOT_FOUND)
I know the view itself works because I tested that separately. The data is definitely present since helper.reset_test_db() puts it there (I know I should be using fixtures but this is for testing so I went with the simple route). The same error occurs for POST and other commands or when I use Django's TestCase instead of APITestCase. While this is my first time using Django's TestCase, I read both the Django and Django rest documents but can't seem to figure out this issue.
The view in your case is a class-based view.
So you have to add it to the urlconfig with as_view:
url(r'^user/1/$', startapp.views.get_1.as_view()),
I'm working on a simple webservice in django. This is my first web app in django/python so I wouldn't be surprised if I'm missing something obvious here, but...
I'm currently trying to test my logic for filtering a url.
# Works as expected
response = self.client.post("/mysite/goodurl/")
self.assertEqual(response.status_code, 200)
# Has an exception rather than a 404
response = self.client.post("/mysite/badurl/")
self.assertEqual(response.status_code, 404)
So, the badurl case isn't simply being not found and throwing a 404, instead I'm getting this error:
Traceback (most recent call last):
File "/home/user/me/mysite/tests.py", line 55, in test_add_tracker
response = self.client.post("/mysite/badurl/")
File "/home/path/to/some/bin/dir/freeware/Python/lib/python2.7/site-packages/django/test/client.py", line 449, in post
response = super(Client, self).post(path, data=data, content_type=content_type, **extra)
File "/home/path/to/some/bin/dir/freeware/Python/lib/python2.7/site-packages/django/test/client.py", line 262, in post
return self.request(**r)
File "/home/path/to/some/bin/dir/freeware/Python/lib/python2.7/site-packages/django/core/handlers/base.py", line 111, in get_response
response = callback(request, *callback_args, **callback_kwargs)
File "/home/path/to/some/bin/dir/freeware/Python/lib/python2.7/site-packages/django/views/decorators/csrf.py", line 77, in wrapped_view
return view_func(*args, **kwargs)
TypeError: EtimeFetcher() got an unexpected keyword argument 'alias'
I've tried googling for the EtimeFetcher message, but no luck. Any ideas?
Uou may probably have some view to catch all Http404 errors. It seems that Django however had found a view to execute for the /badurl. Search in your code for statements containing the alias named parameter, like: "alias=yyyy". Or probably some url pattern, contaning 'alias' as extra parameter in urls.py?
I've got a problem... we're writing project using django, and i'm trying to use django.test.client with nose test-framework for tests.
Our code is like this:
from simplejson import loads
from urlparse import urljoin
from django.test.client import Client
TEST_URL = "http://smakly.localhost:9090/"
def test_register():
cln = Client()
ref_data = {"email": "unique#mail.com", "name": "Василий", "website": "http://hot.bear.com", "xhr": "true"}
print urljoin(TEST_URL, "/accounts/register/")
response = loads(cln.post(urljoin(TEST_URL, "/accounts/register/"), ref_data))
print response["message"]
and in nose output I catch:
Traceback (most recent call last):
File "/home/psih/work/svn/smakly/eggs/nose-0.11.1-py2.6.egg/nose/case.py", line 183, in runTest
self.test(*self.arg)
File "/home/psih/work/svn/smakly/src/smakly.tests/smakly/tests/frontend/test_profile.py", line 25, in test_register
response = loads(cln.post(urljoin(TEST_URL, "/accounts/register/"), ref_data))
File "/home/psih/work/svn/smakly/parts/django/django/test/client.py", line 313, in post
response = self.request(**r)
File "/home/psih/work/svn/smakly/parts/django/django/test/client.py", line 225, in request
response = self.handler(environ)
File "/home/psih/work/svn/smakly/parts/django/django/test/client.py", line 69, in __call__
response = self.get_response(request)
File "/home/psih/work/svn/smakly/parts/django/django/core/handlers/base.py", line 78, in get_response
urlconf = getattr(request, "urlconf", settings.ROOT_URLCONF)
File "/home/psih/work/svn/smakly/parts/django/django/utils/functional.py", line 273, in __getattr__
return getattr(self._wrapped, name)
AttributeError: 'Settings' object has no attribute 'ROOT_URLCONF'
My settings.py file does have this attribute.
If I get the data from the server with standard urllib2.urllopen().read() it works in the proper way.
Any ideas how I can solve this case?
Probably want django-nose if you want to use nose.
http://github.com/jbalogh/django-nose
I would recommend using the TestCase class
http://docs.djangoproject.com/en/dev/topics/testing/
http://www.djangoproject.com/documentation/models/test_client/
Shameless self-promotion: exactly for those reasons I made test library that enables you to test application with urllib2.
Docs are there: http://readthedocs.org/docs/django-sane-testing/en/latest/
Example of what You might want to do is, for example, in django-http-digest-tests