24

Just upgraded to Django 1.3. My test suite is now showing a bunch of useless warnings like this every time I check that a given URL is 404. It didn't do that under Django 1.2.

For example, let's say we have views and URLs wired up such that this test passes:

def test_foo(self):
    response = self.client.get('/foo/bar/')
    self.assertEqual(response.status_code, 200)
    response = self.client.get('/foo/bar2/')
    self.assertEqual(response.status_code, 404)

Although the test passes, the 404 (which we expect) triggers a warning to the console:

.WARNING Not Found: /foo/bar2/

This is just useless noise; I have about 30 of them in one of my current test suites.

Is there a way to silence those just during tests? I'd like to leave them on during normal operation. And I don't think I want to filter out all warnings from the 'django.request' logger.

slinkp
  • 3,406
  • 2
  • 21
  • 18
  • Important distinction I've found: these are not "warnings", only "warnings". That is, Python has [warnings](https://docs.python.org/3/library/warnings.html) that are essentially exceptions but treated slightly differently, and there are a bunch of utility methods for controlling these (`warnings.catch_warnings`, `django.test.utils.ignore_warnings`). But those don't help here, because this bypasses the warning mechanism and directly logs a message to the logger; that message just happens to have the string "WARNING" in it. – Xiong Chiamiov May 19 '23 at 17:32

3 Answers3

22

I know some years passed by but for others looking for this question the following might be helpful.

Based on @jterrace solution you could easily implement a decorator function like this:

import logging

def prevent_request_warnings(original_function):
    """
    If we need to test for 404s or 405s this decorator can prevent the
    request class from throwing warnings.
    """
    def new_function(*args, **kwargs):
        # raise logging level to ERROR
        logger = logging.getLogger('django.request')
        previous_logging_level = logger.getEffectiveLevel()
        logger.setLevel(logging.ERROR)

        # trigger original function that would throw warning
        original_function(*args, **kwargs)

        # lower logging level back to previous
        logger.setLevel(previous_logging_level)

    return new_function

Using this you could write your code like this:

@prevent_request_warnings
def test_foo(self):
    response = self.client.get('/foo/bar/')
    self.assertEqual(response.status_code, 200)
    response = self.client.get('/foo/bar2/')
    self.assertEqual(response.status_code, 404)
Nico Knoll
  • 389
  • 3
  • 4
22

The warning is coming from here: https://github.com/django/django/blob/main/django/core/handlers/base.py

What you want to do is set the logging threshold of the 'django.request' module to something above WARNING (e.g. ERROR) at the beginning of the test, and then set it back afterward.

Try something like this:

import logging

#before tests
logger = logging.getLogger('django.request')
previous_level = logger.getEffectiveLevel()
logger.setLevel(logging.ERROR)

#after tests
logger.setLevel(previous_level)
Xiong Chiamiov
  • 13,076
  • 9
  • 63
  • 101
jterrace
  • 64,866
  • 22
  • 157
  • 202
  • Yeah I was hoping for a lazier solution. That's the proper way to do it, but for now I'll just do the same config in settings.LOGGING. – slinkp Jun 17 '11 at 02:53
  • Yeah, the problem is that the exception clause outputs the warning no matter what, so unfortunately the only way to get it to stop is to lower the logging level. – jterrace Jun 17 '11 at 16:38
  • 9
    Don't be lazy. I'm the poor bastard who will have to clean up that code after you've moved onto your next job. – John Mee Jun 22 '11 at 00:42
  • 1
    You'll be happy to know that I added the level setting/restore to my TestCase's setUp() / tearDown(). John, when do you start? ;) – slinkp Sep 27 '11 at 16:06
8

A solution implementing @jterrace's solution is to decrease the log level in the setUp and tearDown methods:

class NotFoundTests(TestCase):
    def setUp(self) -> None:
        """Reduce the log level to avoid errors like 'not found'"""
        logger = logging.getLogger("django.request")
        self.previous_level = logger.getEffectiveLevel()
        logger.setLevel(logging.ERROR)

    def tearDown(self) -> None:
        """Reset the log level back to normal"""
        logger = logging.getLogger("django.request")
        logger.setLevel(self.previous_level)

Just copy/paste to the TestCase class with tests that check for pages that don't get found.

Karl Lorey
  • 1,536
  • 17
  • 21