Django unit test client response has empty context
Asked Answered
T

3

14

I have a unit test that's failing in an assertion that passes in another test in the same test case class.

Here's the passing test:

def test_home(self):
    c = Client()
    resp = c.get('/')
    self.assertEqual(resp.status_code, 200)
    self.assertTrue('a_formset' in resp.context)

Here's the failing test:

def test_number_initial_number_of_forms(self):
    c = Client()
    resp = c.get('/')
    self.assertEqual(resp.context['a_formset'].total_form_count(), 1)

In the second test, I get the error TypeError: 'NoneType' object has no attribute '__getitem__'.

If I execute the second test as

def test_number_initial_number_of_forms(self):
    c = Client()
    resp = c.get('/')
    self.assertTrue('a_formset' in resp.context)
    self.assertEqual(resp.context['a_formset'].total_form_count(), 1)

I get the error TypeError: argument of type 'NoneType' is not iterable. I've confirmed via print statements in the second test that the response.content contains the page I expect to get, that the status code is correct, and that the template is correct. But the response's context is consistently None in the second test.

I'm running my Django unit tests through the standard "python manage.py test ..." interface, so I don't believe I'm running into the "context is empty from the shell" issue.

What's going on with this?

Edit:

If I add print type(resp.context['a_formset']) to each test, for the working test I get <class 'django.forms.formsets.AFormFormSet'>. For the non-working test, I get TypeError: 'NoneType' object has no attribute '__getitem__' again.

Triode answered 18/1, 2013 at 4:47 Comment(4)
@sneawo Yeah, it's a formset.Triode
In both your working and non-working tests, temporarily add the line print type(resp.context['a_formset']). You might not be getting what you're expecting.Handley
@EvanPorter Added the results of that to the question. Predictably (and unfortunately), I got the object for the working test and the missing attribute error for the non-working test. :-/Triode
Strange that context would be None if the template has successfully rendered using that context. PyCharm supports debugging Django tests, I would use this tool and stick a breakpoint at the end of your view, in any middleware that has a process_response() hook, and any template context processors that you may have added, tracking the context.Upkeep
C
5

Today I run into the same issue. The second test gets same page has nothing in response.context

I made a research and found that 1) test client uses signals to populate context, 2) my view method is not called for the second test

I turned on a debugger and found that the guilty one is 'Cache middleware'. Knowing that I found this ticket and this SO question (the latter has a solution).

So, in short: the second request is served from cache, not from a view, thus a view is not executed and test-client doesn't get the signal and have no ability to populate context.

I can not disable cache middleware for my project, so I added next hack-lines into my settings:

if 'test' in sys.argv:
   CACHE_MIDDLEWARE_SECONDS = 0

Hope this helps someone

Combes answered 19/6, 2013 at 11:6 Comment(0)
C
9

It's because you ran into some error, exited the shell and restarted it.

But you forgot to start environment...

from django.test.utils import setup_test_environment
>>> setup_test_environment()

That was my problem. Hope it works...

Cyruscyst answered 30/1, 2014 at 10:47 Comment(0)
C
5

Today I run into the same issue. The second test gets same page has nothing in response.context

I made a research and found that 1) test client uses signals to populate context, 2) my view method is not called for the second test

I turned on a debugger and found that the guilty one is 'Cache middleware'. Knowing that I found this ticket and this SO question (the latter has a solution).

So, in short: the second request is served from cache, not from a view, thus a view is not executed and test-client doesn't get the signal and have no ability to populate context.

I can not disable cache middleware for my project, so I added next hack-lines into my settings:

if 'test' in sys.argv:
   CACHE_MIDDLEWARE_SECONDS = 0

Hope this helps someone

Combes answered 19/6, 2013 at 11:6 Comment(0)
C
1

You can also clear cache manually by calling cache.clear() inside a test method:

from django.core.cache import cache
import pytest


class TestPostView:

    @pytest.mark.django_db(transaction=True)
    def test_index_post(self, client, post):
        cache.clear()
        response = client.get('/')
Cobwebby answered 20/8, 2021 at 20:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.