views:

271

answers:

2

I am using Django's unit testing apparatus (manage.py test), which is throwing an error and halting when the code generates a warning. This same code when tested with the standard Python unittest module, generates warnings but continues code execution through them.

A little research shows that Python can be set to raise warnings to exceptions, which I suppose would cause the testing framework to think an error had occurred. Unfortunately, the Django documentation on testing is a little light on the definition of an "error", or how to modify the handling of warnings.

So: Is the Django unit testing framework setup to raise warnings to errors by default? Is there some facility in Django for changing this behavior? If not, does anyone have any suggestions for how I can Django to print out the errors but continue code execution? Or have I completely misdiagnosed the problem?

UPDATE: The test code is halting on warnings thrown by calls on MySQLdb. Those calls are made by a module which throws the same warnings when tested under the Python unittest framework, but does not halt. I'll think about an efficient way of trying to replicate the situation in code terse enough to post.

ANSWER:

A little more research reveals this behavior is related to Django's MySQL backend:

/usr/...django/.../mysql/base.py:

if settings.DEBUG:
    ...
    filterwarnings("error", category=Database.Warning)

When I change settings.py so DEBUG = False, the code throws the warning but does not halt.

I hadn't previously encountered this behavior in Django because my database calls are generated by backend of my own. Since I didn't call the Django backend, I didn't reset the handling of the warnings, and the code continued despite the warnings. The Django test framework surely calls the Django backend -- it does all sorts of things with the database -- and that call would reset the warning handling before my code is called.

+2  A: 

Given the updated info, I'm inclined to say that this is the right thing for Django to be doing; MySQL's warnings can indicate any number of things up to and including loss of data (e.g., MySQL will warn and silently truncate if you try to insert a value larger than a column can hold), and that's the sort of thing you'd want to find out about when testing. So probably your best bet is to look at the warnings it's generating and change your code so that it no longer causes those warnings to happen.

James Bennett
The behavior seems reasonable to me, but don't consider myself sufficiently expert to say. The warning I'm getting is "table doesn't exist" as a response to "drop table if exists" -- I don't know how to write SQL to avoid that one. I hope my notes discuss the situation as a behavior, which doesn't happen to suit my particular situation, rather than a bug.
chernevik
Although MySQL will warn on truncate and continue the insert, they consider that ok by default. I personally think Django should defer to the DB vendor in these cases. If Django feels it should escalate a warning (truncated data) to an error (data impacted in an adverse way), it should either set the SQLMODE to traditional, tell the user that MySQL is not in traditional mode during startup, or just note the issue in the docs.http://dev.mysql.com/doc/refman/5.0/en/server-sql-mode.html#sqlmode_traditional
Adam Nelson
Adam, Django has its own, DB-independent, standards for what is and is not correct behavior (e.g., we also go to some lengths to ensure referential integrity regardless of whether the MySQL storage engine does). And, really, that's the correct thing to do, regardless of whether a DB vendor feels that losing/corrupting data is acceptable.
James Bennett
A: 

From the documentation, Django testing doesn't run DEBUG mode: http://docs.djangoproject.com/en/dev/topics/testing/#other-test-conditions, so I'm not sure that the code you referenced is causing the warnings/error. How are you triggering 'table doesn't exist' SQL as part of the ./manage.py test process?

michaeljoseph