host-specific request breaks unrelated test cases - django

I have the following tests:
from django.test import TestCase
from django.core.urlresolvers import reverse
class TestA(TestCase):
def test_a(self):
reverse('view1')
class TestB(TestCase):
def test_b(self):
self.client.get('/view2/', HTTP_HOST='second.test.net')
class TestC(TestCase):
def test_c(self):
reverse('view1')
TestA and TestB run successfully, but TestC breaks with
..E
======================================================================
ERROR: test_c (dhtest.tests.test_view2.TestC)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/home/phihag/dhtest/dhtest/tests/test_view2.py", line 14, in test_c
reverse('view1')
File "/home/phihag/.local/share/virtualenvs/dhtest---IwXRQ3/lib/python3.6/site-packages/django/urls/base.py", line 91, in reverse
return force_text(iri_to_uri(resolver._reverse_with_prefix(view, prefix, *args, **kwargs)))
File "/home/phihag/.local/share/virtualenvs/dhtest---IwXRQ3/lib/python3.6/site-packages/django/urls/resolvers.py", line 497, in _reverse_with_prefix
raise NoReverseMatch(msg)
django.urls.exceptions.NoReverseMatch: Reverse for 'view1' not found. 'view1' is not a valid view function or pattern name.
----------------------------------------------------------------------
Ran 3 tests in 0.006s
FAILED (errors=1)
But when I comment out TestB, TestC works! How do I fix this problem?
I'm using django-hosts with the following configuration:
from django.conf import settings
from django_hosts import patterns, host
host_patterns = patterns(
'',
host(
r'second\.test\.net',
'dhtest.secondurls',
name='second'
),
host(
r'(\w+)',
'dhtest.urls',
name='default'
),
)
and fairly simple URL files:
# dhtest/urls.py
from django.conf.urls import url
from django.http import HttpResponse
urlpatterns = [
url(r'^view1/', lambda _: HttpResponse('This is view1'), name='view1'),
]
# dhtest/secondurls.py
from django.conf.urls import url
from django.http import HttpResponse
urlpatterns = [
url(r'^view2/', lambda _: HttpResponse('view2 on another host')),
]
For reference, here is the full project.

This is a bug in Django-hosts. In its response middleware, django-hosts clobbers the thread-wide urlconf to that of the request.
In production, that is not a problem, because Django resets the urlconf to the default for every request, or the request-specific one.
But during the tests, after TestB there are no more requests coming, and reverse (and a bunch of other URL-related functions) are using the urlconf for a different host.
To work around this, restore the urlconf after any requests to specific hosts, like this:
class TestB(TestCase):
def test_b(self):
self.client.get('/view2/', HTTP_HOST='second.test.net')
def tearDown(self):
from django.urls.base import set_urlconf
set_urlconf(None)

Related

Django Rest Framework URL for custom action not working

I've got the following custom action in my view:
class OrderAPIViewSet(viewsets.ViewSet):
def create(self, request):
print("Here: working")
#action(detail=True, methods=['post'])
def add(self, request, *arg, **kwargs):
print("HERE in custom action")
order = self.get_object()
print(order)
my app's urls.py is:
from rest_framework import routers
from .views import OrderAPIViewSet
router = routers.DefaultRouter()
router.register(r'orders', OrderAPIViewSet, basename='order')
urlpatterns = router.urls
So in my test when I try to access orders/post it works, but when I try to access orders/{pk}/add it fails. I mean, the reverse itself is failing:
ORDERS_LIST_URL = reverse('order-list')
ORDERS_ADD_URL = reverse('order-add')
class PublicOrderApiTests(TestCase):
def test_sample_test(self):
data = {}
res = self.client.post(ORDERS_ADD_URL, data, format='json')
as I said before, I've got a separate test where I use ORDERS_LIST_URL like this:
res = self.client.post(ORDERS_LIST_URL, data, format='json')
but when running the test I'm getting the following error:
ImportError: Failed to import test module: orders.tests Traceback
(most recent call last): File
"/usr/local/lib/python3.7/unittest/loader.py", line 436, in
_find_test_path
module = self._get_module_from_name(name) File "/usr/local/lib/python3.7/unittest/loader.py", line 377, in
_get_module_from_name
import(name) File "/app/orders/tests.py", line 22, in
ORDERS_ADD_URL = reverse('order-add') File "/usr/local/lib/python3.7/site-packages/django/urls/base.py", line 87,
in reverse
return iri_to_uri(resolver._reverse_with_prefix(view, prefix, *args, **kwargs)) File "/usr/local/lib/python3.7/site-packages/django/urls/resolvers.py",
line 685, in _reverse_with_prefix
raise NoReverseMatch(msg) django.urls.exceptions.NoReverseMatch: Reverse for 'order-add' with no arguments not found. 2 pattern(s)
tried: ['orders/(?P[^/.]+)/add\.(?P[a-z0-9]+)/?$',
'orders/(?P[^/.]+)/add/$']
---------------------------------------------------------------------- Ran 1 test in 0.000s
FAILED (errors=1)
according to the documentation I shouldn't need to register this endpoint, the router is supposed to do it by itself. What am I missing?
The first thing that you've missed is pk in your reverse. Since the add API needs a pk of your Order object, you need to pass it to reverse function. For example:
order_add_url = reverse('order-add', kwargs={'pk': 1})
print(order_add_url) # which will print '/orders/1/add/'
So I think you should move this part to the body of PublicOrderApiTests's methods since you need a dynamic url per test object.
Another problem is that the ViewSet class does not support self.get_object() and if you want to use this method you should either have your own method or use rest framework GenericViewSet (i.e. from rest_framework.viewsets import GenericViewSet and inherit from this class instead of ViewSet) then you can access the get_object() method. You can also read more about generic views in rest framework docs.

can't use reverse url function in django

tests.py
from django.test import TestCase
from django.urls import reverse
from rest_framework import status
from .models import User
class UserCreateAPIViewTestCase(APITestCase):
def setUp(self):
super().setUp()
self.url = reverse("admins")
def test_user_creating(self):
user_data = {}
response = self.client.post(self.url, user_data, format="json")
self.assertEqual(response.status_code,
status.HTTP_403_FORBIDDEN)
2.urls.py
from django.urls import path
from django.conf.urls import include
from rest_framework_nested.routers import SimpleRouter
from apps.users.views import (
CreateProviderViewSet,
LoginViewSet,
UserViewSet,
ProviderViewSet,
ClientViewSet,
LoginAsViewSet
)
app_name = 'users'
router = SimpleRouter(trailing_slash=False)
router.register("admins", UserViewSet, base_name='admins')
router.register("providers", ProviderViewSet, base_name='providers')
router.register("clients", ClientViewSet, base_name='clients')
router.register("login", LoginViewSet, base_name='auth')
router.register("login-as", LoginAsViewSet)
urlpatterns = [
path('', include(router.urls)),
]
when I run python .\manage.py test apps.users.tests
This error occurs
ERROR: test_user_creating (apps.users.tests.UserCreateAPIViewTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
File "C:\Users\vu.tran\Desktop\kona-server\apps\users\tests.py", line 19, in setUp
self.url = reverse("admins")
File "C:\Users\vu.tran\Desktop\kona-server\env\lib\site-packages\django\urls\base.py", line 90, in reverse
return iri_to_uri(resolver._reverse_with_prefix(view, prefix, *args, **kwargs))
File "C:\Users\vu.tran\Desktop\kona-server\env\lib\site-packages\django\urls\resolvers.py", line 668, in _reverse_with_prefix
raise NoReverseMatch(msg)
django.urls.exceptions.NoReverseMatch: Reverse for 'admins' not found. 'admins' is not a valid view function or pattern name.
my structure folders like this my folders
I wonder why cannot get reverse("admins")
Do you have any idea?
According to the documentation, if you want to access the list view, the name for the url should be admins-list. The name of the argument for your register function may also be basename instead of base_name.

Django Testing: URL mapping to the Class Based View

I'm new to Django testing so trying basic testing codes. But it is showing one error in second test class
Tests.py
from django.test import TestCase,Client
from .views import PostList
from django.urls import resolve
class SmokeTest2(TestCase):
def test_math(self):
self.assertEqual(1+1,2)
class HomePageTest(TestCase):
def test_root_url_resolves_to_home_page_view(self):
found = resolve('/')
print({'found':found})
self.assertEqual(found.func(), PostList)
views.py
class PostList(ListView):
model = Post
template_name = 'home.html'
urls.py
urlpatterns = [
path('',views.PostList.as_view(),name ='list'),
]
When i am printing found its showing the o/p
{'found': ResolverMatch(func=blog.views.PostList, args=(), kwargs={}, url_name=list, app_names=[], namespaces=[])}
But still I am getting this error
(blog_env) PS D:\django\blog_env\mysite> python manage.py test
D:\django\blog_env\mysite
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
{'found': ResolverMatch(func=blog.views.PostList, args=(), kwargs={}, url_name=list, app_names=[], namespaces=[])}
E.
======================================================================
ERROR: test_root_url_resolves_to_home_page_view (blog.tests.HomePageTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "D:\django\blog_env\mysite\blog\tests.py", line 19, in test_root_url_resolves_to_home_page_view
self.assertEqual(found.func(), PostList)
TypeError: view() missing 1 required positional argument: 'request'
----------------------------------------------------------------------
Ran 2 tests in 0.069s
FAILED (errors=1)
Destroying test database for alias 'default'...
I was stung by this issue just now, ended up finding the solution in the documentation
class-based views need to be compared by name, as the functions generated by as_view() won't be equal due to different object ids, so the assertion should look like the below:
from django.test import TestCase
from django.urls import resolve
from .views import HomePageView
class HomePageViewViewTest(TestCase):
def test_resolve_to_home_page_view(self):
resolver = resolve('/')
self.assertEqual(resolver.func.__name__, HomePageView.as_view().__name__)
from django.urls import resolve, reverse
class HomePageViewViewTest(TestCase):
def test_resolve_to_home_page_view(self):
resolver = resolve('/')
self.assertEqual(resolver.func.view_class, HomePageView)
You can try this, it worked for me!
Since you are testing a Class based View, from the Traceback it can be seen that it's missing the request object. You can use the RequestFactory provided by the django.test package. Better read the following RequestFactory Documentation to get a good view of it. It will solve your problem.
from django.urls import resolve, reverse
class HomePageTest(TestCase):
def test_root_url_resolves_to_home_page_view(self):
response = self.client.get(resolve('/'))
response = self.client.get(reverse('your_app_name:list'))
self.assertEqual(response.status_code, 200)

Django: Why does this test fail?

I an new to Django and to Test Driven Devolopment as well.
After working through the tutorials in the 1.11 official documentation
I am starting my first app: wos_2017_2
This test fails and I cannot figure out why:
import unittest
from django.test import TestCase
from django.test import Client
from .models import *
from .views import *
class SimpleTest(unittest.TestCase):
def test_index(self):
client = Client()
response = client.get('/')
self.assertEqual(response.status_code, 200)
FAIL: test_index (wos_2017_2.tests.SimpleTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/home/js/django/wos/wos_2017_2/tests.py", line 16, in test_index
self.assertEqual(response.status_code, 200)
AssertionError: 404 != 200
This link in the browser works without a problem:
http://localhost:8000/wos_2017_2/
In the shell (run from the project root):
>>> from django.test import Client
>>> client = Client()
>>> response = client.get('/')
>>> response = client.get('wos_2017_2/index')
Not Found: /wos_2017_2index
>>> response = client.get('wos_2017_2/')
Not Found: /wos_2017_2
>>> response = client.get('/wos_2017_2/')
>>> response = client.get('/wos_2017_2/index/')
Not Found: /wos_2017_2/index/
>>> response = client.get('/wos_2017_2/')
>>> response.status_code
200
in wos_2017_1.urls.py:
from . import views
from django.conf.urls import url
urlpatterns = [
url(r'^$', views.index, name='index'),
]
client.get('/') is failing because you haven't defined a URL pattern for ^$ in your root url config (the one in the same directory as your settings.py).
You have included your urls with:
url(r'^wos_2017_2/', include('wos_2017_2.urls')),
and in that urls you have
url(r'^$', views.index, name='index'),
Therefore in your test you should use response = client.get('/wos_2017_2/')

How to make a simple Django URLconf and reverse() on it for a test? (getting TypeError: unhashable type: 'list')

I am writing convenience code which calls django.core.urlresolvers.reverse() to generate links. However, I can't seem to write a simple URLconf for a quick test.
This is what I tried:
>>> from django.conf.urls import patterns, url
>>> conf = patterns('', url(r'^foo/$', lambda request: None, name='foo'))
>>> from django.core.urlresolvers import reverse
>>> reverse('foo', conf)
Traceback (most recent call last):
File "<console>", line 1, in <module>
File ".../env/local/lib/python2.7/site-packages/django/core/urlresolvers.py", line 445, in reverse
resolver = get_resolver(urlconf)
File ".../env/local/lib/python2.7/site-packages/django/utils/functional.py", line 27, in wrapper
if mem_args in cache:
TypeError: unhashable type: 'list'
I'm using Django 1.5 on Python 2.7.
I would consider creating a test_urls.py module
from django.conf.urls import patterns, include, url
urlpatterns = patterns('', url(r'^foo/$', lambda request: None, name='foo'))
then give the path to test_urls.py when you call reverse in your test.
reverse('foo', 'path.to.test_urls')
If you really want to create the urlconf in your test, you need to make sure it has a urlpatterns attribute. The following works in Django 1.4.
from django.conf.urls import patterns, url
from django.core.urlresolvers import reverse
class MockUrlConf(object):
urlpatterns = patterns('', url(r'^foo/$', lambda request: None, name='foo'))
reverse('foo', MockUrlConf)
reverse method's urlconf param is a string indicating the name of the module containing the urls. You may call it like this:
reverse('foo', 'your_app.urls' )
Now, I don't know how exactly change this behavior, you may create some urls.py for testing and calling them, but this seems hardly tight to the URL modules names in your settings.py.
The documentation isn-t very useful, it says basically you won't need to set urlconf param. So I would listen to it and try another way.
Good luck!