Unit test failing with 302 instead of 200, using Django's TestCase class

2,611 views
Skip to first unread message

palmeida

unread,
Aug 4, 2009, 12:26:56 PM8/4/09
to Django users
Hi,

I'm running a test that fails when using Django's TestCase class, but
not when running TestCase from unittest. This is my test class:

class LoggedInUser(TestCase):
fixtures = ['myflow']
def setUp(self):
self.client = Client()
try:
User.objects.create_user('user', 'm...@nowhere.com', 'user')
except IntegrityError:
pass
self.client.login(username='user', password='user')

def test_experiment_view(self):
response = self.client.get('/myflow/experiment/1')
print response.context
self.failUnlessEqual(response.status_code, 200)

It fails with:

AssertionError: 302 != 200

when using Django's TestCase class, but everything is fine when using
unittest. Also, when it fails everything in response seems fine except
for response.context (and response.content, of course).
response.context gets lots of variables, like MEDIA_URL, LANGUAGES and
others. I have no idea where that is coming from.

This is the relevant line from urls.py:

(r'^experiment/(?P<experiment_id>\d+)$',
'bioinformatics.myflow.views.view_experiment'),

And this is the view:

def view_experiment(request, experiment_id):
contact = request.user.id
# Filtering on contact and pk to ensure users can only see their
own data
exp = get_object_or_404(Experiment.objects.filter
(exp_contact=contact),
pk=experiment_id)

list = generate_list()

files = exp.fcs_set.exclude(name__startswith='myflow_meta')
num_files = len(files)
files = files.order_by('btim')
fcs_list = paginate(files,'fcs_per_page',request)
# Create variable to include (or not) subset column in fcs file
list
if len(subsets) > 1:
subset_header = True
else:
subset_header = False
return render_to_response('myflow/view_experiment.html',
{'experiment': exp,
'subsets': subsets,
'parameters': list,
'num_files': num_files,
'fcs_list': fcs_list,
'subset_header': subset_header
}
)

Thanks for any help. I'm not sure what I can do next to find out what
is going on.
Paulo Almeida

Alex Gaynor

unread,
Aug 4, 2009, 12:31:19 PM8/4/09
to django...@googlegroups.com
It looks like you have teh APPEND_SLASH setting set to True (the
default), so Django adds a / to the end of that URL. Try righting the
test to that URL with a trailing slash and see if it works.

Alex

--
"I disapprove of what you say, but I will defend to the death your
right to say it." -- Voltaire
"The people's good is the highest law." -- Cicero
"Code can always be simpler than you think, but never as simple as you
want" -- Me

Paulo Almeida

unread,
Aug 4, 2009, 12:44:54 PM8/4/09
to django...@googlegroups.com

Hi Alex,

When I do that I get a 404 instead of a 302. I also tried changing urls.py to have a trailing slash but that also gives me a 404. Byt the way, one of the variables I get in response.context is:

{'request_path': u'/myflow/experiment/1/'}

Thanks for the reply,
Paulo

Karen Tracey

unread,
Aug 4, 2009, 1:18:51 PM8/4/09
to django...@googlegroups.com
On Tue, Aug 4, 2009 at 12:26 PM, palmeida <igcbioin...@gmail.com> wrote:

Hi,

I'm running a test that fails when using Django's TestCase class, but
not when running TestCase from unittest. This is my test class:

class LoggedInUser(TestCase):
   fixtures = ['myflow']

Note fixtures will not be loaded when you run this as a unittest.TestCase.
 

   def setUp(self):
       self.client = Client()
       try:
           User.objects.create_user('user', 'm...@nowhere.com', 'user')
       except IntegrityError:
           pass
       self.client.login(username='user', password='user')

Is there a reason, other than to allow this to run as a unittest.TestCase, why are you setting client yourself in setUp()?  That's done for you by Django's TestCase.  If you are going to be using fixtures and the test Client, this really should be a django.test.TestCase.  I am not sure that going down the road of seeing if you can't get it to run as a unittest.TestCase by manually doing in the test things that django.test.TestCase also does is the best way of debugging whatever issue the test is having when running as a django.test.TestCase.  I would back up and take out anything you have added that allows this to run as a unittest.TestCase and start over with trying to debug the real issue directly.
 

   def test_experiment_view(self):
       response = self.client.get('/myflow/experiment/1')
       print response.context
       self.failUnlessEqual(response.status_code, 200)

It fails with:

AssertionError: 302 != 200

when using Django's TestCase class, but everything is fine when using
unittest.

302 is a redirect.  It would be useful to know where it's trying to redirect to.  For that, you could put a:

print response['location']

into the test.
 
Also, when it fails everything in response seems fine except
for response.context (and response.content, of course).
response.context gets lots of variables, like MEDIA_URL, LANGUAGES and
others. I have no idea where that is coming from.

Since your view below, from a quick scan, doesn't ever return a redirect your GET is apparently being routed to some other view entirely.  I'd guess that view is including all of this other stuff in its context.
 

This is the relevant line from urls.py:

(r'^experiment/(?P<experiment_id>\d+)$',
    'bioinformatics.myflow.views.view_experiment'),

I cannot recreate what you are seeing based on just this information.  I suspect there is more to your urls.py, or something, that is coming into play.  Finding out where you are getting redirected to would be a first step in figuring out what is going wrong.

Karen

Paulo Almeida

unread,
Aug 4, 2009, 1:33:48 PM8/4/09
to django...@googlegroups.com
Hi Karen,

You're right, I had some unnecessary lines that I used only for unittest. I commented them out now. Printing response['location'] gives me this:

http://testserver/myflow/experiment/1/

You're also right that there is no Redirect in my view, so I don't know where that is coming from. I guess I'll have to figure it out, because it seems to be the key to the problem.

Thanks for the reply,
Paulo Almeida

Karen Tracey

unread,
Aug 4, 2009, 1:45:07 PM8/4/09
to django...@googlegroups.com
On Tue, Aug 4, 2009 at 1:33 PM, Paulo Almeida <igcbioin...@gmail.com> wrote:
Hi Karen,

You're right, I had some unnecessary lines that I used only for unittest. I commented them out now. Printing response['location'] gives me this:

http://testserver/myflow/experiment/1/

You're also right that there is no Redirect in my view, so I don't know where that is coming from. I guess I'll have to figure it out, because it seems to be the key to the problem.

So it does appear to be APPEND_SLASH that is causing the redirect, since the only difference between that and what you requested is the added slash on the end.  Which implies the version without the added slash is not matching the url pattern you cited:


 (r'^experiment/(?P<experiment_id>\d+)$',
    'bioinformatics.myflow.views.view_experiment'),

even though that pattern doesn't require a trailing slash.  That pattern, though, also has no 'myflow' component.  Where is that coming from?  There seems to be more to your url configuration than you have made clear here.

Karen

Paulo Almeida

unread,
Aug 4, 2009, 4:27:24 PM8/4/09
to django...@googlegroups.com
Hi,

The myflow part comes from the parent urls.py, which includes myflow/urls.py when matching ^myflow (sorry, didn't think of that). I did try to comment out the CommonMiddleware line, in settings.py, and the error persisted, but maybe I should try adding APPEND_SLASH = False. I will then look at response['location'] with and without APPEND_SLASH to see if that is doing anything.

Thanks again,
Paulo

Paulo Almeida

unread,
Aug 5, 2009, 5:45:32 AM8/5/09
to django...@googlegroups.com
Ok, so with APPEND_SLASH = False , I get a 404 (so no response['location']) , but the response.context still has all the variables like MEDIA_URL, LANGUAGES, LANGUAGE_BIDI, and not the ones it should have. It still has also:

{'request_path': u'/myflow/experiment/1'}

I also tested another view, with URL:

response = self.client.get('/myflow/experiments')

This one works, and it has a list of experiments which links to the view that isn't working. The link in response.content is:

a href="/myflow/experiment/1"

as it should be, and as it works outside tests. Also, from that /myflow/experiments response.content I can tell that the fixtures are being loaded, so it should not be a problem of experiment 1 not existing.

If you can suggest anything else I may try, please do.

Thank you,
- Paulo Almeida

Malcolm Tredinnick

unread,
Aug 5, 2009, 5:57:55 AM8/5/09
to django...@googlegroups.com
On Wed, 2009-08-05 at 10:45 +0100, Paulo Almeida wrote:
> Ok, so with APPEND_SLASH = False , I get a 404 (so no
> response['location']) , but the response.context still has all the
> variables like MEDIA_URL, LANGUAGES, LANGUAGE_BIDI, and not the ones
> it should have. It still has also:
>
> {'request_path': u'/myflow/experiment/1'}
>
> I also tested another view, with URL:
>
> response = self.client.get('/myflow/experiments')
>
> This one works, and it has a list of experiments which links to the
> view that isn't working. The link in response.content is:
>
> a href="/myflow/experiment/1"
>
> as it should be, and as it works outside tests. Also, from
> that /myflow/experiments response.content I can tell that the fixtures
> are being loaded, so it should not be a problem of experiment 1 not
> existing.
>
> If you can suggest anything else I may try, please do.

It's time to use the scientific method. Make the smallest self-contained
example possible that exhibits the problem. A single test case in a file
that uses Django.TestCase. A single view that does nothing but returns a
constant string. A single URL pattern, etc. Remove everything that you
possibly can. If you find you cannot remove a particular chunk of
functionality (a URL pattern or a view or a template or whatever), then
you know where to look for the problem.

You have a lot of things in flight here all at once. Throw as many
overboard as you and eliminate the moving parts. What's left is the
problem you're trying to solve.

Regards,
Malcolm

Paulo Almeida

unread,
Aug 5, 2009, 6:11:28 AM8/5/09
to django...@googlegroups.com
Hi,

Thanks for the suggestion. I will do that now, but in the mean time I realized the 404 is probably happening because of the get_object_or_404 in the view, which means the experiment really isn't there, or at least not with id 1. I confirmed that by replacing get_object_or_404 with:

exp = Experiment.objects.get(pk=experiment_id)

which returns:

DoesNotExist: Experiment matching query does not exist.

That is weird, because the id in the href comes from experiment.id, and everything is working fine outside testing, but the response.context is also weird, so I'll do what you suggest to try and narrow down the problem.

Thanks,
Paulo

Paulo Almeida

unread,
Aug 5, 2009, 12:50:38 PM8/5/09
to django...@googlegroups.com
Mystery solved.

The fixtures were not being imported after all. My list_experiments view imports files from a certain folder, and that was the experiment data being listed. Then the database was rolled back before the view_experiment test started, and naturally the experiment with id 1 was not there anymore. I suppose that rollback is the difference that made the test work with unittest.

Thanks for all the help,
Paulo 
Reply all
Reply to author
Forward
0 new messages