I am using a simple unit test based test runner to test my Django application.
My application itself is configured to use a basic logger in settings.py using:
<Since you are in Django, you could add these lines to your settings.py:
import sys
import logging
if len(sys.argv) > 1 and sys.argv[1] == 'test':
logging.disable(logging.CRITICAL)
That way you don't have to add that line in every setUp()
on your tests.
You could also do a couple of handy changes for your test needs this way.
There is another "nicer" or "cleaner" way to add specifics to your tests and that is making your own test runner.
Just create a class like this:
import logging
from django.test.simple import DjangoTestSuiteRunner
from django.conf import settings
class MyOwnTestRunner(DjangoTestSuiteRunner):
def run_tests(self, test_labels, extra_tests=None, **kwargs):
# Don't show logging messages while testing
logging.disable(logging.CRITICAL)
return super(MyOwnTestRunner, self).run_tests(test_labels, extra_tests, **kwargs)
And now add to your settings.py file:
TEST_RUNNER = "PATH.TO.PYFILE.MyOwnTestRunner"
#(for example, 'utils.mytest_runner.MyOwnTestRunner')
This lets you do one really handy modification that the other approach doesn't, which is to make Django just tests the applications that you want. You can do that by changing the test_labels
adding this line to the test runner:
if not test_labels:
test_labels = ['my_app1', 'my_app2', ...]
Sometimes you want the logs and sometimes not. I have this code in my settings.py
import sys
if '--no-logs' in sys.argv:
print('> Disabling logging levels of CRITICAL and below.')
sys.argv.remove('--no-logs')
logging.disable(logging.CRITICAL)
So if you run your test with the --no-logs
options you'll get only the critical
logs:
$ python ./manage.py tests --no-logs
> Disabling logging levels of CRITICAL and below.
It's very helpful if you want speedup the tests on your continuous integration flow.
If you don't want it repeatedly turn it on/off in setUp() and tearDown() for unittest (don't see the reason for that), you could just do it once per class:
import unittest
import logging
class TestMyUnitTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
logging.disable(logging.CRITICAL)
@classmethod
def tearDownClass(cls):
logging.disable(logging.NOTSET)
I like Hassek's custom test runner idea. It should be noted that DjangoTestSuiteRunner
is no longer the default test runner in Django 1.6+, it has been replaced by the DiscoverRunner
. For default behaviour, the test runner should be more like:
import logging
from django.test.runner import DiscoverRunner
class NoLoggingTestRunner(DiscoverRunner):
def run_tests(self, test_labels, extra_tests=None, **kwargs):
# disable logging below CRITICAL while testing
logging.disable(logging.CRITICAL)
return super(NoLoggingTestRunner, self).run_tests(test_labels, extra_tests, **kwargs)
I've found that for tests within unittest
or similar a framework, the most effective way to safely disable unwanted logging in unit tests is to enable/disable in the setUp
/tearDown
methods of a particular test case. This lets one target specifically where logs should be disabled. You could also do this explicitly on the logger of the class you're testing.
import unittest
import logging
class TestMyUnitTest(unittest.TestCase):
def setUp(self):
logging.disable(logging.CRITICAL)
def tearDown(self):
logging.disable(logging.NOTSET)
Some of my tests contain assertions about log output, so changing the log level breaks them. Instead, I changed my Django LOGGING
settings to use a NullHandler when running tests:
if 'test' in sys.argv:
_LOG_HANDLERS = ['null']
else:
_LOG_HANDLERS = ['console']
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'simple': {
'format': '%(levelname)s %(message)s'
},
},
'handlers': {
'null': {
'level': 'DEBUG',
'class': 'logging.NullHandler',
},
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
'formatter': 'simple',
},
},
'loggers': {
'django': {
'handlers': _LOG_HANDLERS,
'propagate': True,
'level': 'INFO',
},
}
}