Làm cách nào tôi có thể tắt ghi nhật ký trong khi chạy thử nghiệm đơn vị trong Python Django?


168

Tôi đang sử dụng một trình chạy thử nghiệm đơn vị dựa trên thử nghiệm đơn giản để kiểm tra ứng dụng Django của tôi.

Bản thân ứng dụng của tôi được cấu hình để sử dụng một trình ghi nhật ký cơ bản trong settings.txt bằng cách sử dụng:

logging.basicConfig(level=logging.DEBUG)

Và trong mã ứng dụng của tôi bằng cách sử dụng:

logger = logging.getLogger(__name__)
logger.setLevel(getattr(settings, 'LOG_LEVEL', logging.DEBUG))

Tuy nhiên, khi chạy không đáng tin cậy, tôi muốn tắt ghi nhật ký để nó không làm lộn xộn kết quả kiểm tra của tôi. Có cách nào đơn giản để tắt đăng nhập theo cách toàn cầu, để trình ghi nhật ký cụ thể của ứng dụng không ghi nội dung ra bảng điều khiển khi tôi chạy thử nghiệm không?


Làm thế nào bạn kích hoạt đăng nhập trong khi chạy thử nghiệm? và tại sao bạn không sử dụng django LOGGING?
dinois

Câu trả lời:


249
logging.disable(logging.CRITICAL)

sẽ vô hiệu hóa tất cả các cuộc gọi đăng nhập với mức độ ít nghiêm trọng hơn hoặc bằng CRITICAL. Ghi nhật ký có thể được kích hoạt lại với

logging.disable(logging.NOTSET)

42
Điều này có thể rõ ràng nhưng đôi khi tôi thấy hữu ích khi nói rõ lợi ích của những người đọc khác: Bạn sẽ đặt cuộc gọi đến logging.disable(từ câu trả lời được chấp nhận) ở đầu tests.pyứng dụng của bạn đang thực hiện đăng nhập.
CJ Gaconnet

7
Tôi đã kết thúc cuộc gọi trong setUp () nhưng quan điểm của bạn được thực hiện tốt.
shreddd

trong phương thức setUp () của thử nghiệm của bạn hoặc trong thử nghiệm thực tế tạo ra các thông điệp tường trình mà bạn muốn ẩn.
qris

10
Và trong tearDown()phương pháp của bạn : logging.disable(logging.NOTSET)đặt đăng nhập trở lại vị trí gọn gàng.
mlissner

34
Đưa nó vào init .py của testsmô-đun là rất hữu ích.
toabi

46

Vì bạn đang ở Django, bạn có thể thêm các dòng này vào cài đặt của bạn:

import sys
import logging

if len(sys.argv) > 1 and sys.argv[1] == 'test':
    logging.disable(logging.CRITICAL)

Bằng cách đó, bạn không phải thêm dòng đó vào mỗi setUp()bài kiểm tra của mình.

Bạn cũng có thể thực hiện một vài thay đổi tiện dụng cho bài kiểm tra của mình theo cách này.

Có một cách khác "đẹp hơn" hoặc "sạch hơn" để thêm chi tiết cụ thể vào các bài kiểm tra của bạn và đó là tạo ra người chạy thử nghiệm của riêng bạn.

Chỉ cần tạo một lớp như thế này:

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)

Và bây giờ thêm vào tập tin cài đặt của bạn:

TEST_RUNNER = "PATH.TO.PYFILE.MyOwnTestRunner"
#(for example, 'utils.mytest_runner.MyOwnTestRunner')

Điều này cho phép bạn thực hiện một sửa đổi thực sự tiện dụng mà cách tiếp cận khác không có, đó là làm cho Django chỉ kiểm tra các ứng dụng mà bạn muốn. Bạn có thể làm điều đó bằng cách thay đổi test_labelsthêm dòng này vào trình chạy thử:

if not test_labels:
    test_labels = ['my_app1', 'my_app2', ...]

Chắc chắn - đặt nó trong settings.py sẽ làm cho nó toàn cầu.
shreddd

7
đối với Django 1.6+, vui lòng kiểm tra câu trả lời @alukach.
Hassek

2
Đôi khi trong các bài kiểm tra đơn vị, tôi muốn khẳng định rằng một lỗi đã được ghi lại do đó phương pháp này không lý tưởng. Tuy nhiên, đó một câu trả lời tốt.
Sardathrion - chống lại lạm dụng SE

23

Có cách nào đơn giản để tắt đăng nhập theo cách toàn cầu, để trình ghi nhật ký cụ thể của ứng dụng không ghi nội dung ra bảng điều khiển khi tôi chạy thử nghiệm không?

Các câu trả lời khác ngăn "viết nội dung ra bàn điều khiển" bằng cách đặt toàn cầu cơ sở hạ tầng ghi nhật ký để bỏ qua mọi thứ. Điều này hoạt động nhưng tôi thấy nó quá cùn một cách tiếp cận. Cách tiếp cận của tôi là thực hiện thay đổi cấu hình, chỉ thực hiện những gì cần thiết để ngăn chặn nhật ký thoát ra khỏi bảng điều khiển. Vì vậy, tôi thêm một bộ lọc ghi nhật ký tùy chỉnh vào settings.py:

from logging import Filter

class NotInTestingFilter(Filter):

    def filter(self, record):
        # Although I normally just put this class in the settings.py
        # file, I have my reasons to load settings here. In many
        # cases, you could skip the import and just read the setting
        # from the local symbol space.
        from django.conf import settings

        # TESTING_MODE is some settings variable that tells my code
        # whether the code is running in a testing environment or
        # not. Any test runner I use will load the Django code in a
        # way that makes it True.
        return not settings.TESTING_MODE

Và tôi định cấu hình ghi nhật ký Django để sử dụng bộ lọc:

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'filters': {
        'testing': {
            '()': NotInTestingFilter
        }
    },
    'formatters': {
        'verbose': {
            'format': ('%(levelname)s %(asctime)s %(module)s '
                       '%(process)d %(thread)d %(message)s')
        },
    },
    'handlers': {
        'console': {
            'level': 'DEBUG',
            'class': 'logging.StreamHandler',
            'filters': ['testing'],
            'formatter': 'verbose'
        },
    },
    'loggers': {
        'foo': {
            'handlers': ['console'],
            'level': 'DEBUG',
            'propagate': True,
        },
    }
}

Kết quả cuối cùng: khi tôi đang thử nghiệm, không có gì vào bảng điều khiển, nhưng mọi thứ khác vẫn giữ nguyên.

Tại sao làm điều này?

Tôi thiết kế mã chứa các hướng dẫn ghi nhật ký chỉ được kích hoạt trong các trường hợp cụ thể và sẽ xuất dữ liệu chính xác mà tôi cần để chẩn đoán nếu có sự cố. Do đó, tôi kiểm tra rằng họ làm những gì họ phải làm và do đó vô hiệu hóa hoàn toàn việc đăng nhập là không khả thi đối với tôi. Tôi không muốn tìm một khi phần mềm đang được sản xuất mà những gì tôi nghĩ sẽ được ghi lại không được ghi lại.

Hơn nữa, một số người chạy thử nghiệm (ví dụ Mũi) sẽ ghi lại các bản ghi trong quá trình kiểm tra và xuất ra phần có liên quan của nhật ký cùng với một lỗi kiểm tra. Nó rất hữu ích trong việc tìm ra lý do tại sao một bài kiểm tra thất bại. Nếu đăng nhập hoàn toàn bị tắt, thì không có gì có thể bị bắt.


"Bất kỳ người chạy thử nào tôi sử dụng sẽ tải mã Django theo cách làm cho nó đúng." Thú vị ... thế nào?
webtweaker 17/03/2016

Tôi có một test_settings.pytập tin nằm bên cạnh dự án của tôi settings.py. Nó được thiết lập để tải settings.pyvà thực hiện một số thay đổi như được đặt TESTING_MODEthành True. Người chạy thử nghiệm của tôi được tổ chức sao cho test_settingsmô-đun được tải cho cài đặt dự án Django. Có nhiều cách này có thể được thực hiện. Tôi thường đi với thiết lập biến môi trường DJANGO_SETTINGS_MODULEthành proj.test_settings.
Louis

Điều này là tuyệt vời và làm chính xác những gì tôi muốn. Ẩn việc ghi nhật ký trong thời gian không xác định cho đến khi có lỗi xảy ra - sau đó Django Nose chọn đầu ra và in với lỗi. Hoàn hảo. Kết hợp nó với điều này để xác định xem thử nghiệm đơn vị có hoạt động hay không.
rrauenza

21

Tôi thích ý tưởng chạy thử nghiệm tùy chỉnh của Hassek. Cần lưu ý rằng DjangoTestSuiteRunnerkhông còn là người chạy thử mặc định trong Django 1.6+, nó đã được thay thế bởi DiscoverRunner. Đối với hành vi mặc định, người chạy thử nên giống như:

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)

Tôi tìm thấy giải pháp của bạn sau khi thử rất nhiều thứ. Tuy nhiên, tôi không thể đặt biến TEST_RUNNER trong cài đặt vì nó không thể nhập mô-đun có tệp test_runner.
Thỏ Bunny

Âm thanh như một vấn đề nhập khẩu. Bạn có đang đặt TEST_RUNNER thành đường dẫn chuỗi tới người chạy (không phải mô-đun Python thực tế) không? Ngoài ra, người chạy của bạn nằm ở đâu? Tôi có của tôi trong một ứng dụng riêng có tên helpers, chỉ có các tiện ích không nhập từ bất kỳ nơi nào khác trong dự án.
alukach

5

Tôi đã thấy rằng đối với các thử nghiệm trong unittesthoặc tương tự một khung, cách hiệu quả nhất để vô hiệu hóa ghi nhật ký không mong muốn trong các thử nghiệm đơn vị là bật / tắt trong setUp/ tearDownphương thức của một trường hợp thử nghiệm cụ thể. Điều này cho phép một mục tiêu cụ thể nơi các bản ghi nên bị vô hiệu hóa. Bạn cũng có thể làm điều này một cách rõ ràng trên bộ ghi chép của lớp bạn đang kiểm tra.

import unittest
import logging

class TestMyUnitTest(unittest.TestCase):
    def setUp(self):
        logging.disable(logging.CRITICAL)

    def tearDown(self):
        logging.disable(logging.NOTSET)

4

Tôi đang sử dụng một công cụ trang trí phương pháp đơn giản để vô hiệu hóa đăng nhập chỉ trong một phương pháp thử nghiệm cụ thể.

def disable_logging(f):

    def wrapper(*args):
        logging.disable(logging.CRITICAL)
        result = f(*args)
        logging.disable(logging.NOTSET)

        return result

    return wrapper

Và sau đó tôi sử dụng nó như trong ví dụ sau:

class ScenarioTestCase(TestCase):

    @disable_logging
    test_scenario(self):
        pass

3

Có một số phương pháp đẹp và sạch để tạm dừng đăng nhập trong các thử nghiệm với unittest.mock.patchphương thức.

foo.py :

import logging


logger = logging.getLogger(__name__)

def bar():
    logger.error('There is some error output here!')
    return True

tests.py :

from unittest import mock, TestCase
from foo import bar


class FooBarTestCase(TestCase):
    @mock.patch('foo.logger', mock.Mock())
    def test_bar(self):
        self.assertTrue(bar())

python3 -m unittest testssẽ không sản xuất đăng nhập.


1

Đôi khi bạn muốn các bản ghi và đôi khi không. Tôi có mã này trongsettings.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)

Vì vậy, nếu bạn chạy thử nghiệm với các --no-logstùy chọn, bạn sẽ chỉ nhận được các criticalbản ghi:

$ python ./manage.py tests --no-logs
> Disabling logging levels of CRITICAL and below.

Nó rất hữu ích nếu bạn muốn tăng tốc các bài kiểm tra trên luồng tích hợp liên tục của mình.


1

Nếu bạn không muốn nó bật / tắt liên tục trong setUp () và tornDown () cho không đáng tin cậy (không thấy lý do cho điều đó), bạn chỉ có thể thực hiện một lần cho mỗi lớp:

    import unittest
    import logging

    class TestMyUnitTest(unittest.TestCase):
        @classmethod
        def setUpClass(cls):
            logging.disable(logging.CRITICAL)
        @classmethod
        def tearDownClass(cls):
            logging.disable(logging.NOTSET)

1

Trong trường hợp tôi muốn tạm thời loại bỏ một trình ghi nhật ký cụ thể, tôi đã viết một trình quản lý bối cảnh nhỏ mà tôi thấy hữu ích:

from contextlib import contextmanager
import logging

@contextmanager
def disable_logger(name):
    """Temporarily disable a specific logger."""
    logger = logging.getLogger(name)
    old_value = logger.disabled
    logger.disabled = True
    try:
        yield
    finally:
        logger.disabled = old_value

Sau đó, bạn sử dụng nó như:

class MyTestCase(TestCase):
    def test_something(self):
        with disable_logger('<logger name>'):
            # code that causes the logger to fire

Điều này có lợi thế là logger được kích hoạt lại (hoặc đặt trở lại trạng thái trước đó) sau khi withhoàn thành.


1

Bạn có thể đặt nó trong thư mục cấp cao nhất cho __init__.pytập tin kiểm tra đơn vị . Điều này sẽ vô hiệu hóa đăng nhập toàn cầu trong bộ kiểm tra đơn vị.

# tests/unit/__init__.py
import logging

logging.disable(logging.CRITICAL)

0

Trong trường hợp của tôi, tôi có một tệp cài đặt settings/test.pyđược tạo riêng cho mục đích thử nghiệm, đây là giao diện:

from .base import *

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': 'test_db'
    }
}

PASSWORD_HASHERS = (
    'django.contrib.auth.hashers.MD5PasswordHasher',
)

LOGGING = {}

Tôi đặt một biến môi trường DJANGO_SETTINGS_MODULE=settings.testđể /etc/environment.


0

Nếu bạn có các mô-đun initaliser khác nhau để kiểm tra, phát triển và sản xuất thì bạn có thể vô hiệu hóa mọi thứ hoặc chuyển hướng nó trong trình khởi tạo. Tôi có local.txt, test.py và sản xuất tất cả đều kế thừa từ common.y

common.py thực hiện tất cả các cấu hình chính bao gồm đoạn mã này:

LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
    'django.server': {
        '()': 'django.utils.log.ServerFormatter',
        'format': '[%(server_time)s] %(message)s',
    },
    'verbose': {
        'format': '%(levelname)s %(asctime)s %(module)s %(process)d %(thread)d %(message)s'
    },
    'simple': {
        'format': '%(levelname)s %(message)s'
    },
},
'filters': {
    'require_debug_true': {
        '()': 'django.utils.log.RequireDebugTrue',
    },
},
'handlers': {
    'django.server': {
        'level': 'INFO',
        'class': 'logging.StreamHandler',
        'formatter': 'django.server',
    },
    'console': {
        'level': 'DEBUG',
        'class': 'logging.StreamHandler',
        'formatter': 'simple'
    },
    'mail_admins': {
        'level': 'ERROR',
        'class': 'django.utils.log.AdminEmailHandler'
    }
},
'loggers': {
    'django': {
        'handlers': ['console'],
        'level': 'INFO',
        'propagate': True,
    },
    'celery.tasks': {
        'handlers': ['console'],
        'level': 'DEBUG',
        'propagate': True,
    },
    'django.server': {
        'handlers': ['django.server'],
        'level': 'INFO',
        'propagate': False,
    },
}

Sau đó, trong test.txt tôi có cái này:

console_logger = Common.LOGGING.get('handlers').get('console')
console_logger['class'] = 'logging.FileHandler
console_logger['filename'] = './unitest.log

Điều này thay thế trình xử lý bàn điều khiển bằng FileHandler và có nghĩa là vẫn đăng nhập nhưng tôi không phải chạm vào cơ sở mã sản xuất.


0

Nếu bạn đang sử dụng pytest:

Vì pytest chụp các thông điệp nhật ký và chỉ hiển thị chúng cho các thử nghiệm thất bại, bạn thường không muốn vô hiệu hóa bất kỳ ghi nhật ký nào. Thay vào đó, sử dụng một settings.pytệp riêng cho các bài kiểm tra (ví dụ test_settings.py:) và thêm vào đó:

LOGGING_CONFIG = None

Điều này nói với Django bỏ qua việc cấu hình đăng nhập hoàn toàn. Các LOGGINGthiết lập sẽ bị bỏ qua và có thể được gỡ bỏ từ các cài đặt.

Với phương pháp này, bạn không nhận được bất kỳ bản ghi nào cho các bài kiểm tra đã qua và bạn nhận được tất cả các bản ghi có sẵn cho các bài kiểm tra thất bại.

Các bài kiểm tra sẽ chạy bằng cách đăng nhập được thiết lập bởi pytest. Nó có thể được cấu hình theo ý thích của bạn trong pytestcài đặt (ví dụ tox.ini:). Để bao gồm các thông báo nhật ký mức gỡ lỗi, sử dụng log_level = DEBUG(hoặc đối số dòng lệnh tương ứng).

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.