4

I have an Django API that I build quickly, and now I want to add some tests to ensure is stable. The problem is that Django simply refuses to let me access any resource on the server. It can't find any URL.

I have some models, but this test is with the User model.

# auth/urls.py
urlpatterns = [
...
    path(
        "user/<str:email>/",
        views.UserView.as_view(),
        name="public staff user",
    ),
...
]

# auth/views/UserView.py (Folder working as a submodule)
... # Needed imports

from django.contrib.auth import get_user_model

UserModel = get_user_model()

class UserView(
    generics.RetrieveAPIView,
    generics.UpdateAPIView,
    generics.DestroyAPIView,
):
    serializer_class = UserSerializer
    lookup_field = "email"

    def get_queryset(self):
        return UserModel.objects.all()

# auth/tests/tests.py (Folder workign as a submodule, tests.py is a temporary name)
from django.test import TestCase
from django.urls import reverse
from rest_framework.test import APIClient, APITestCase

from authentication.models import User

import os

# Create your tests here.


class TestTestCase(APITestCase):
    def setUp(self):
        self.staff_user = User.objects.create(
            email="test@account.me", is_staff=True, is_active=True
        )
        self.staff_user_password = "staff_password"
        self.staff_user.set_password(self.staff_user_password)

    def test_testing(self):
        print(User.objects.all())
        url = reverse("public staff user", kwargs={"email": self.staff_user.email})
        print(url)

        response = self.client.get(
            url,
        )
        print(response.content)

I have a few unused imports, but those don't matter. I create a User instance, and change it's password. Then, in the test, I try to retrieve it's data.

I can confirm the user exists (the shell returns <QuerySet [<User: Employee#000>]>) and the url works (/api/user/test@account.me/ works with another email in the dev database).

However, response.content returns the following:

# Escaped for ease of reading
b'\n<!doctype html>\n<html lang="en">\n<head>\n
  <title>Not Found</title>\n</head>\n<body>\n
  <h1>Not Found</h1>
<p>The requested resource was not found on this server.</p>
\n</body>\n</html>\n'

I confirmed that the client can access any url, whether reversed or hard-coded. Can't POST or PATCH to anything either.

I've been scratching my head for a while now, but I can't find what I'm missing


Edit 1: I've added an edited version of the view. The logic is the same, but I'm not allowed to share the exact code.


Edit 2: I get the UserModel from Django's get_user_model. Added clarification


Edit 3: format='json' does not change anything

Xceptional Bro
  • 101
  • 1
  • 8

2 Answers2

3

OMG. I didn't fix it, but I found a workaround. For everyone going through the same hell as me, here's a possible fix.

  1. You're gonna want to use a Factory instead of a Client. APIRequestFactory works just fine
  2. The Client couldn't find the view, but reverse and resolve are able. So you need to get the view function from those.
  3. Finally, you're gonna want to generate a request using the Factory, and a response using the view function

Working example:

from django.urls import reverse, resolve
from rest_framework.test import APIRequestFactory, APITestCase

from yourapp import models

class TestTestCase(APITestCase):
    def setUp(self):
        self.staff_user = models.YourModel.objects.create(**yourdata)

    def test_testing(self):
        factory = APIRequestFactory()
        url = reverse("your reverse url name", kwargs=kwargs)
        sol = resolve(url)
        view = sol.func

        request = f.get(url) # works with every method
        response = view(request, **kwargs)
        response.render()
        print(response.content) # Finally! Response, my beloved!

Worst this about this workaround is that I need to pass the kwargs twice. I'll end up making a helper method and done

Xceptional Bro
  • 101
  • 1
  • 8
  • It wasn't exactly what I was looking for but your code did help me remember why I kept getting this same error ... doh! – Harlin Dec 08 '21 at 21:55
0

Sometimes you can get this error if you are using:

response = client.get('my-view-name', args=[0, 1])

instead of ...

response = client.get(reverse('my-view-name', args=[0, 1]))

Moral: make sure you use reverse() from django.urls. Hope this might help someone frantically searching for the answer who may have come here searching the exact error but context is a little different.

Harlin
  • 1,059
  • 14
  • 18