Opened 2 months ago

Closed 2 months ago

#35851 closed New feature (wontfix)

django.test.client.ClientMixin._login doest not set enviorn like REMOTE_ADDR can cause test failures in certain situations

Reported by: elonzh Owned by:
Component: Testing framework Version: dev
Severity: Normal Keywords:
Cc: elonzh Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description (last modified by elonzh)

Our service listens for the user_logged_in signal to log the user's IP information, but when using TestClient.login/force_login, the absence of REMOTE_ADDR results in an error.

By reviewing the source code, I found that TestClient.login/force_login(https://github.com/django/django/blob/main/django/test/client.py#L869-L882) creates an empty HttpRequest, which behaves differently from django.test.client.Client.request(https://github.com/django/django/blob/main/django/test/client.py#L401-L436).

Therefore, I believe this is an issue that needs to be addressed.


I'd like to create a patch if this ticket is confirmed.

Change History (2)

in reply to:  description comment:1 by elonzh, 2 months ago

Description: modified (diff)

comment:2 by Natalia Bidart, 2 months ago

Resolution: wontfix
Status: newclosed
Type: BugNew feature
Version: dev

Hello elonzh! Thank you for creating this ticket. I think I understand your use case, but in my opinion, when testing a Django app's code, there are two different concerns:

  1. Ensure that your app's business logic is correct when a user goes thru the login dance, and
  2. Given a logged in user, ensure some other properties and qualities of your service.

For the first case, I would think that you app has tests asserting over the recorded IP, after having exercised a "full login process". This would be similar to visiting the login page, entering credentials (i.e. emulating a POST), and being redirected to a given location. So, in practice, something like (untested):

from django.contrib.auth.models import User
from django.test import TestCase
from django.urls import reverse


class LoginTestCase(TestCase):

    def test_basic(self):
        creds = {"username": "test", "password": "securepassword"}
        user = User.objects.create_user(**creds)
        ip_addr = "200.100.200.142"

        response = self.client.post(
            reverse("login"), data=creds, REMOTE_ADDR=ip_addr)
        self.assertRedirects
            response, "/accounts/profile/", fetch_redirect_response=False)
        self.assertMessages(response, "You are successfully logged in.")
        self.assertIPRecorded(user, expected=ip_addr)

For the second case, the test needs a logged in user as a precondition of the test, for which the only thing that matters is the authenticated user in the request, which is what login gives you. Client.login is not meant to mimic a complete login process, for that you need to do something similar to what I put in item 1.

In summary, Client.request emulates a "true" HTTP request, but Client.login ensures you get an authenticated user in the request (which is not the same as saying "the user did an HTTP login dance").

Given the above, I'll close this ticket accordingly, but if you disagree, you can consider starting a new conversation on the Django Forum, where you'll reach a wider audience and likely get extra feedback. More information in the documented guidelines for requesting features.

Note: See TracTickets for help on using tickets.
Back to Top