How to follow Django redirect using django-pytest?
Asked Answered
O

2

6

In setting up a ArchiveIndexView in Django I am able to successfully display a list of items in a model by navigating to the page myself.

When going to write the test in pytest to verify navigating to the page "checklist_GTD/archive/" succeeds, the test fails with the message:

>       assert response.status_code == 200
E       assert 301 == 200
E        +  where 301 = <HttpResponsePermanentRedirect status_code=301, "text/html; charset=utf-8", url="/checklist_GTD/archive/">.status_code

test_archive.py:4: AssertionError

I understand there is a way to follow the request to get the final status_code. Can someone help me with how this done in pytest-django, similar to this question? The documentation on pytest-django does not have anything on redirects. Thanks.

Obedience answered 9/1, 2018 at 11:8 Comment(2)
Does this help? docs.djangoproject.com/en/2.0/topics/testing/tools/…Gunthar
Using 'assertRedirects' might be a good solution to try... but that would involve mixing in "test" from django rather than using an existing fixture from 'pytest-django.' If that is the only way, I wouldn't be surprised! But I'm looking to see if I'm missing something really easy like just adding a decorator to the function.Obedience
O
0

UPDATE: I'm getting downvoted into oblivion but I still think my answer is better so let me explain.

I still think there is a problem with Shacker's answer, where you can set follow=True and get a response code of 200 but not at the URL you expect. For example, you could get redirected unexpectedly to the login page, follow and get a response code of 200.

I understand that I asked a question on how to accomplish something with pytest and the reason I'm getting downvoted is because I provided an answer using Django's built-in TestCase class. However, the correct answer for the test is/was more important to me at the time than exclusively using pytest. As noted below, my answer still works with pytest's test discovery so I think the answer is still valid. After all, pytest is built upon Django's built-in TestCase. And my answer asserts the response code of 200 came from where I expected it to come from.

The best solution would be to modify pytest to include the expected_url as a parameter. If anyone is up for doing this I think it would be a big improvement. Thanks for reading.

ORIGINAL CONTENT:

Answering my own question here. I decided to include final expected URL using the built-in Django testing framework's assertRedirects and verify that it (1) gets redirected initially with 302 response and (2) eventually succeeds with a code 200 at the expected URL.

from django.test import TestCase, Client

def test_pytest_works():
    assert 1==1

class Test(TestCase):
    def test_redirect(self):
        client = Client()
        response = client.get("/checklist_GTD/archive/")
        self.assertRedirects(response, "/expected_redirect/url", 302, 200)

Hat tip to @tdsymonds for pointing me in the right direction. I appreciated Shacker's answer but I have seen in some scenarios the redirect result being 200 when the page is redirected to an undesirable URL. With the solution above I am able to enforce the redirect URL, which pytest-django does not currently support.

Please note: This answer is compliant with the auto-discover feature of pytest-django and is thus not incompatible (it will auto-discover both pytest-django and Django TestCase tests).

Obedience answered 18/3, 2018 at 23:27 Comment(3)
Downvoted because providing a "Just use Django native tests" for a person asking about a pytest question is not particularly helpful.Ysabel
Hi Shacker. Thanks for the explanation. I answered my own question if you didn't notice, due to the inactivity. And it still solves the problem because Django's built-in testing is compatible with the pytest command. Thanks for your answer, also! I'll check to see if it works.Obedience
For fear of being downvoted I won't make this an "answer". But until pytest-django fully supports the HTTP redirect feature of SimpleTestCase I just append the urls with something like assert response.url == reverse('login') + '?next=' + reverse('my_app:create').Nevillenevin
Y
9

pytest-django provides both an unauthenticated client and a logged-in admin_client as fixtures. Really simplifies this sort of thing. Assuming for the moment that you're using admin_client because you just want to test the redirect as easily as possible, without having to log in manually:

def test_something(admin_client):
    response = admin_client.get(url, follow=True)
    assert response.status_code == 200

If you want to log in a standard user:

def test_something(client):
    # Create user here, then:
    client.login(username="foo", password="bar")
    response = client.get(url, follow=True)
    assert response.status_code == 200

By using follow=True in either of these, the response.status_code will equal the return code of the page after the redirect, rather than the access to the original URL. Therefore, it should resolve to 200, not 301.

I think it's not documented in pytest-django because the option is inherited from the Django test client it subclasses from (making requests).

Ysabel answered 21/3, 2018 at 6:6 Comment(7)
Do you have any idea why this 'follow' parameter is not in the pytest-django documentation? If you noticed above in my question, I referenced the fact that I didn't see anything on redirection in the documentation, and that is still the case. pytest-django.readthedocs.io/en/latest/…Obedience
Edited the answer to include reason it's not documented. But it would be nice to see it documented - I've contributed doc patches to pytest-django before and they've been accepted. In any case, if you found my answer helpful, please consider marking it accepted, or upvoting.Ysabel
Your answer is great, and very close to what I was looking for. Let's see if we can edit it a bit more if possible. In my answer below, I specified what I expected the redirect URL to be. Is there a way to do that with the follow parameter? For example, "final_url" or something (e.g., response = admin_client.get(url, follow=True, final_url="/expected_redirect/url")? ) This will be useful if you want to ensure you properly redirected an unauthenticated client to the login page, and not to another page they should not have access to.Obedience
Also, you're missing a closing double quote (") after bar below (won't let me make this trivial edit)Obedience
@ScottSkiles just wondering if you found a way to assert the redirect in pytest only, using something like assertRedirect? or did you end up importing from SimpleTestCase?Unseam
@ScottSkiles I actually got what I was looking for in the comments of this answer https://mcmap.net/q/1157820/-how-to-test-redirection-in-django-using-pytestUnseam
For the record I still think there is a problem with this answer, where you can set follow=True and get a response code of 200 but not at the URL you expect. For example, you could get redirected unexpectedly to the login page, follow, and get a response code of 200 when you land on "login" or whatever when you expected something else.Obedience
O
0

UPDATE: I'm getting downvoted into oblivion but I still think my answer is better so let me explain.

I still think there is a problem with Shacker's answer, where you can set follow=True and get a response code of 200 but not at the URL you expect. For example, you could get redirected unexpectedly to the login page, follow and get a response code of 200.

I understand that I asked a question on how to accomplish something with pytest and the reason I'm getting downvoted is because I provided an answer using Django's built-in TestCase class. However, the correct answer for the test is/was more important to me at the time than exclusively using pytest. As noted below, my answer still works with pytest's test discovery so I think the answer is still valid. After all, pytest is built upon Django's built-in TestCase. And my answer asserts the response code of 200 came from where I expected it to come from.

The best solution would be to modify pytest to include the expected_url as a parameter. If anyone is up for doing this I think it would be a big improvement. Thanks for reading.

ORIGINAL CONTENT:

Answering my own question here. I decided to include final expected URL using the built-in Django testing framework's assertRedirects and verify that it (1) gets redirected initially with 302 response and (2) eventually succeeds with a code 200 at the expected URL.

from django.test import TestCase, Client

def test_pytest_works():
    assert 1==1

class Test(TestCase):
    def test_redirect(self):
        client = Client()
        response = client.get("/checklist_GTD/archive/")
        self.assertRedirects(response, "/expected_redirect/url", 302, 200)

Hat tip to @tdsymonds for pointing me in the right direction. I appreciated Shacker's answer but I have seen in some scenarios the redirect result being 200 when the page is redirected to an undesirable URL. With the solution above I am able to enforce the redirect URL, which pytest-django does not currently support.

Please note: This answer is compliant with the auto-discover feature of pytest-django and is thus not incompatible (it will auto-discover both pytest-django and Django TestCase tests).

Obedience answered 18/3, 2018 at 23:27 Comment(3)
Downvoted because providing a "Just use Django native tests" for a person asking about a pytest question is not particularly helpful.Ysabel
Hi Shacker. Thanks for the explanation. I answered my own question if you didn't notice, due to the inactivity. And it still solves the problem because Django's built-in testing is compatible with the pytest command. Thanks for your answer, also! I'll check to see if it works.Obedience
For fear of being downvoted I won't make this an "answer". But until pytest-django fully supports the HTTP redirect feature of SimpleTestCase I just append the urls with something like assert response.url == reverse('login') + '?next=' + reverse('my_app:create').Nevillenevin

© 2022 - 2024 — McMap. All rights reserved.