Search code examples
djangotestingdjango-rest-frameworkdjango-testing

Django Rest Framework test fails: Expected view to be called with a URL keyword argument


I am running tests on UserDetail view written using Django Rest framework.

urls.py

    url(r'^api/users/', include('calorie_counter.users.urls', namespace='users')),

users/urls.py

    url(r'^(?P<pk>[0-9]+)$', views.UserDetail.as_view(), name='user-detail'),

test_api.py

BaseAPITestCase(APITestCase):
    def setUp(self):
        self.superuser = User.objects.create_superuser('admin', '[email protected]', 'johnpassword')
        self.client.login(username='john', password='johnpassword')
        self.user1 = User.objects.create(username="user1", password="pass", email="[email protected]")


class ReadUserTest(BaseAPITestCase):
# check read permissions
    def test_user_can_read_self_detail(self):
        url = '/api/users/'+str(self.user1.id)
        factory = APIRequestFactory()
        request = factory.get(url)
        force_authenticate(request, self.user1)
        response = (UserDetail.as_view())(request)
        self.assertEqual(response.status_code, status.HTTP_200_OK)

However, running this test, gives me following error. The 'pk' agrument is not getting passed to the UserDetail view.

AssertionError: Expected view UserDetail to be called with a URL keyword argument named "pk". Fix your URL conf, or set the .lookup_field attribute on the view correctly.

How do I test views with URL arguments?

UPDATE:

Now using APIClient instead of factory..

def test_user_can_read_self_detail(self):
    client = APIClient()
    client.login(username='user1', password='pass')

    # response = self.client.get('/api/users/', {'pk': self.user.id})
    response = client.get('/api/users/' + str(self.user1.id))
    self.assertEqual(response.status_code, status.HTTP_200_OK)

Now I am getting following error:

AttributeError: 'AnonymousUser' object has no attribute 'is_manager'

where is manager is an attribute of my custom user model. I guess there is some problem with client authentication. I have session authentication enabled. Still getting this error.

UPDATE: My login wasn't working for APICLient because I was creating user using User.objects.create instead of User.objects.create_user. Changing that fixed the problem. :)


Solution

  • I don't think you need all of this set up you're doing, it's unusual to need to instantiate view classes yourself – perhaps you'll have more success leveraging the test client with something like:

    def test_user_can_read_self_detail(self):
        url = reverse('api:user-detail', kwargs={'pk': self.user.id})
        response = self.client.get(url)
        self.assertEqual(response.status_code, status.HTTP_200_OK)
    

    If you're having issues with authentication, which I suspect may have been what lead you here, you may want to try:

    self.client.login(username='example', password='changeme')
    

    or

    import base64
    self.client.defaults['HTTP_AUTHORIZATION'] = 'Basic ' + base64.b64encode('example:changeme')
    

    I've used both in the past to test authenticated API endpoints.