Search code examples
pythondjangodjango-testingdjango-tests

Django Test Redirect to login with next parameter


I'm trying to Test non-login user redirect to login url. Is there a way to pass get parameter (example 'next') to reverse_lazy? I'm using class-based views.

class SecretIndexViewTest(TestCase):
    url = reverse_lazy('secret_index')
    login_url = reverse_lazy('login')
    client = Client()

    def test_http_status_code_302(self):
        response = self.client.get(self.url)
        self.assertEqual(response.status_code, 302)
        self.assertRedirects(response, self.login_url,)

above fails with

Response redirected to '/login?next=/secret', expected '/login'Expected '/login?next=/secret' to equal '/login'.

tried

class SecretIndexViewTest(TestCase):
    url = reverse_lazy('secret_index')
    login_url = reverse_lazy('login', kwargs={'next': url)
    client = Client()

    def test_http_status_code_302(self):
        response = self.client.get(self.url)
        self.assertEqual(response.status_code, 302)
        self.assertRedirects(response, self.login_url,)

result is no reverse match

django.urls.exceptions.NoReverseMatch: Reverse for 'login' with keyword arguments '{'next': '/secret'}' not found. 1 pattern(s) tried: ['/login$']

changing kwargs to args will result the same error.

I know I can handle it with below, but I'm looking for a better solution.

self.assertRedirects(response, self.login_url + f"?next={self.url}",)

or

login_url = reverse_lazy('login') + f"?next={url}"

Solution

  • As mentioned in the Django documentation, reverse (and reverse_lazy) do not include GET parameters, so your string formatting example is one of the simplest solutions.

    The URLconf searches against the requested URL, as a normal Python string. This does not include GET or POST parameters, or the domain name.

    For example, in a request to https://www.example.com/myapp/, the URLconf will look for myapp/.

    In a request to https://www.example.com/myapp/?page=3, the URLconf will look for myapp/.

    The URLconf doesn’t look at the request method. In other words, all request methods – POST, GET, HEAD, etc. – will be routed to the same function for the same URL.

    If you are worried about encoding your query strings, you can use the tools in the urllib.parse module such as urllib.parse.urlencode to encode your queries before building the full URL by concatenating the path from reverse and the encoded query parameters.

    For simple cases such as tests where you know exactly what data is being passed in, I would be inclined to stick with the simple string formatting.