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.

Community
  • 1
  • 1
Melissa Avery-Weir
  • 1,357
  • 2
  • 16
  • 47
  • @sneawo Yeah, it's a formset. – Melissa Avery-Weir Jan 18 '13 at 14:48
  • 2
    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. – Evan Porter Jan 18 '13 at 17:31
  • @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. :-/ – Melissa Avery-Weir Jan 19 '13 at 17:16
  • 1
    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. – mikewaters Jan 20 '13 at 01:27

3 Answers3

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...

Nabin
  • 11,216
  • 8
  • 63
  • 98
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

Community
  • 1
  • 1
akava
  • 542
  • 7
  • 14
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('/')