From 47bde4b77974d013ad64032e6e6409bdac50cc98 Mon Sep 17 00:00:00 2001 From: MissterHao Date: Fri, 10 Feb 2023 14:14:33 +0800 Subject: [PATCH] feat: Add unittest and mock test for logger module --- .coveragerc | 2 + dysession/logger/__init__.py | 34 +++++++---- dysession/logger/handler/colorful_console.py | 2 - tests/test_logger.py | 61 ++++++++++++++++++++ tests/test_logger_handler_ansi.py | 30 ++++++++++ 5 files changed, 117 insertions(+), 12 deletions(-) create mode 100644 tests/test_logger.py create mode 100644 tests/test_logger_handler_ansi.py diff --git a/.coveragerc b/.coveragerc index 1b8521c..57de4fb 100644 --- a/.coveragerc +++ b/.coveragerc @@ -2,6 +2,8 @@ omit = tests/*.py runtests.py + dysession/logger/handler/colorful_console.py + [report] # Regexes for lines to exclude from consideration exclude_lines = diff --git a/dysession/logger/__init__.py b/dysession/logger/__init__.py index 8bc3d00..ccac192 100644 --- a/dysession/logger/__init__.py +++ b/dysession/logger/__init__.py @@ -2,9 +2,9 @@ import sys from enum import Enum, auto from functools import lru_cache -from typing import Optional +from typing import Literal -from handler.colorful_console import ColorfulConsoleLoggerHandler +from .handler.colorful_console import ColorfulConsoleLoggerHandler class LoggingType(Enum): @@ -23,9 +23,22 @@ def is_tty() -> bool: def get_logger( logger_name: str = "dysession", - logger_type: LoggingType = LoggingType.CONSOLE, + logger_type: Literal[LoggingType.CONSOLE, LoggingType.FILE] = LoggingType.CONSOLE, level: int = logging.DEBUG, ) -> logging.Logger: + """ + This function return a logging.Logger with handlers. + Handlers could be `ColorfulConsoleLoggerHandler`, `StreamHandler`, `FileHandler`. + + ``` + logger = get_logger() + logger.debug("This is a DEBUG log.") + logger.info("This is a INFO log.") + logger.warning("This is a WARNING log.") + logger.critical("This is a CRITICAL log.") + logger.fatal("This is a FATAL log.") + ``` + """ logger = logging.getLogger(logger_name) format = logging.Formatter( @@ -44,14 +57,15 @@ def get_logger( handler.setFormatter(format) logger.addHandler(handler) + else: + logger.warning( + f"Dysession logger had already initialized with logger({logger.handlers})" + ) return logger -if __name__ == "__main__": - logger = get_logger() - logger.debug("This is a DEBUG log.") - logger.info("This is a INFO log.") - logger.warning("This is a WARNING log.") - logger.critical("This is a CRITICAL log.") - logger.fatal("This is a FATAL log.") +__all__ = ( + LoggingType, + get_logger, +) diff --git a/dysession/logger/handler/colorful_console.py b/dysession/logger/handler/colorful_console.py index 9df3d4b..46b4e55 100644 --- a/dysession/logger/handler/colorful_console.py +++ b/dysession/logger/handler/colorful_console.py @@ -19,8 +19,6 @@ class ColorfulConsoleLoggerHandler(logging.StreamHandler): def emit(self, record): try: - # record.msg = "QQQQQQQQQQQQQQ" - # record.levelname = "\033[91m" + record.levelname + "\033[0m" record.levelname = ( LOGLEVEL_TRANSFORM[record.levelno] + f"{record.levelname:>8}" diff --git a/tests/test_logger.py b/tests/test_logger.py new file mode 100644 index 0000000..b923420 --- /dev/null +++ b/tests/test_logger.py @@ -0,0 +1,61 @@ +import logging +import sys +from unittest import mock + +from django.test import TestCase +from parameterized import parameterized + +from dysession.logger import LoggingType, get_logger, is_tty +from dysession.logger.handler.colorful_console import ColorfulConsoleLoggerHandler + + +class LoggerTestCase(TestCase): + def test_get_logger_with_second_logger_type_passed_in(self): + + with self.assertLogs("dysession") as cm: + logger = get_logger() + logger.info("This is a test content") + self.assertIn("This is a test content", "\n".join(cm.output)) + + with self.assertLogs("dysession") as cm: + logger = get_logger(logger_type=LoggingType.FILE) + logger.info("This is a test content") + self.assertIn("WARNING", "\n".join(cm.output)) + + @mock.patch("sys.stdout.isatty") + def test_logger_is_tty_handlers(self, mock_is_tty): + is_tty.cache_clear() + mock_is_tty.return_value = True + + logger = get_logger() + self.assertEqual(len(logger.handlers), 1) + self.assertEqual(logger.name, "dysession") + self.assertIs(type(logger.handlers[0]), ColorfulConsoleLoggerHandler) + + logger.handlers = [] + + @mock.patch("sys.stdout.isatty") + def test_logger_is_not_tty_handlers(self, mock_is_tty): + is_tty.cache_clear() + mock_is_tty.return_value = False + + logger = get_logger() + self.assertEqual(len(logger.handlers), 1) + self.assertEqual(logger.name, "dysession") + self.assertIs(type(logger.handlers[0]), logging.StreamHandler) + + logger.handlers = [] + + def test_logger_file_handler(self): + + logger = get_logger(logger_type=LoggingType.FILE) + + self.assertEqual(len(logger.handlers), 1) + self.assertEqual(logger.name, "dysession") + self.assertIsInstance(logger.handlers[0], logging.FileHandler) + + with self.assertLogs("dysession") as cm: + logger.info("This is a test content") + self.assertIn("This is a test content", "\n".join(cm.output)) + + logger.handlers = [] diff --git a/tests/test_logger_handler_ansi.py b/tests/test_logger_handler_ansi.py new file mode 100644 index 0000000..0537aaa --- /dev/null +++ b/tests/test_logger_handler_ansi.py @@ -0,0 +1,30 @@ +from typing import Any + +from django.test import TestCase +from parameterized import parameterized + +from dysession.logger import get_logger +from dysession.logger.handler.ansi import ANSIColor, colorful_it + + +class ANSITestCase(TestCase): + @parameterized.expand( + [ + [ANSIColor.HEADER], + [ANSIColor.OKBLUE], + [ANSIColor.OKCYAN], + [ANSIColor.OKGREEN], + [ANSIColor.SUCCESSFUL], + [ANSIColor.WARNING], + [ANSIColor.FAIL], + [ANSIColor.DEBUG], + [ANSIColor.BOLD], + [ANSIColor.UNDERLINE], + ] + ) + def test_ansi_colorful_it_func(self, ansi_color: ANSIColor): + + self.assertEqual( + colorful_it(ansi_color, "Content"), + f"{ansi_color.value}Content{ANSIColor.ENDC.value}", + )