{ "info": { "author": "Chad Rosenquist", "author_email": "chadrosenquist@gmail.com", "bugtrack_url": null, "classifiers": [], "description": "\nlogging-test-case\n=================\n\nProduction systems rely heavily upon logging. Unit tests should verify\nlogs are correct. ``unittest.assertLogs()`` allows developers to verify\nlogs are correct. Including this context manager in every test case\nbecomes tiresome. Also, if the test fails, the logs are not displayed.\n\nThis project provides the function decorator ``@capturelogs``.\n``@capturelogs`` is similar to ``unittest.assertLogs()``, but it is a\nfunction decorator, reducing the clutter inside the test function.\n\nThis project provides the class ``LoggingTestCase``, which inherits from\n``unittest.TestCase``. For every test run, logs are automatically\ncaptured to ``self.captured_logs``. If the test fails, the contents of\n``self.captured_logs`` are written to the test output for easy\ndebugging. ``LoggingTestCase`` provides context manager ``assertNoLogs``\nto verify no logs were emitted within the context.\n\n- Use ``@capturelogs`` if only a few tests involve log files.\n- Use ``LoggingTestCase`` if most of the tests involve logs. This\n avoids putting a function decorator for each function.\n\nInstallation\n============\n\nThis package is at pypi at:\n\nhttps://pypi.python.org/pypi/logging-test-case\n\nTo install using pip:\n\n``pip install logging-test-case``\n\nRequirements\n============\n\n- Python 3.4 or higher.\n\n@capturelogs\n============\n\n``capturelogs(logger=None, level=None, display_logs=DisplayLogs.FAILURE, assert_no_logs=False)``\n\n* logger: Name of logger, or an actual logger. Defaults to root logger.\n* level: Log level as a text string. Defaults to 'INFO'.\n* display_logs: Determines when to display logs\n - DisplayLogs.NEVER: Never display the logs. The logs will always be discarded.\n + This is the current behavior of ``unittest.assertLogs()``.\n - DisplayLogs.FAILURE: Display the logs only if the test case fails. (default)\n + This can be useful for debugging test failures because the logs are still written out.\n - DisplayLogs.ALWAYS: Always displays the logs - pass or fail.\n + This can be useful when manually running the tests and the developer wants to visually inspect the logging output.\n* assert_no_logs: If True, raise an AssertionError if any logs are emitted.\n\nExamples are located at: ``examples/capturelogs_example.py``\n\nunittest.assertLogs example\n---------------------------\n\n::\n\n class CaptureLogsExample(unittest.TestCase):\n def test_assert_logs(self):\n \"\"\"Verify logs using built-in self.assertLogs().\"\"\"\n with self.assertLogs('foo', level='INFO') as logs:\n logging.getLogger('foo').info('first message')\n logging.getLogger('foo.bar').error('second message')\n self.assertEqual(logs.output, ['INFO:foo:first message',\n 'ERROR:foo.bar:second message'])\n\n@capturelogs example\n--------------------\n\n::\n\n import unittest\n import logging\n\n from loggingtestcase\n\n\n class CaptureLogsExample(unittest.TestCase):\n @loggingtestcase.capturelogs('foo', level='INFO')\n def test_capture_logs(self, logs):\n \"\"\"Verify logs using @capturelogs decorator.\"\"\"\n logging.getLogger('foo').info('first message')\n logging.getLogger('foo.bar').error('second message')\n\n self.assertEqual(logs.output, ['INFO:foo:first message',\n 'ERROR:foo.bar:second message'])\n\nIn the above example, there is less clutter and indenting inside of the\ntest function. For this simple example, it doesn't matter. But if the\ntest involves multiple patches and ``self.assertRaises`` and many other\ncontext managers, the function becomes crowded very quickly. The\n``@capturelogs`` function decorator allows the developer to reduce the\ncontents and indent level inside of the function.\n\n@capturelogs display example\n----------------------------\n\n::\n\n import unittest\n import logging\n\n import loggingtestcase\n\n\n class CaptureLogsExample(unittest.TestCase):\n @loggingtestcase.capturelogs('foo', level='INFO',\n display_logs=loggingtestcase.DisplayLogs.ALWAYS)\n def test_always_display_logs(self, logs):\n \"\"\"The logs are always written to the original handler(s).\"\"\"\n logging.getLogger('foo').info('first message')\n self.assertTrue(False)\n self.assertEqual(logs.output, ['INFO:foo:first message'])\n\nIn the above example, the test fails, the logs are be displayed.\n\n@capturelogs assert_no_logs example\n-----------------------------------\n\n::\n\n import unittest\n import logging\n\n import loggingtestcase\n\n\n class CaptureLogsExample(unittest.TestCase):\n @loggingtestcase.capturelogs('foo', level='INFO', assert_no_logs=True)\n def test_assert_no_logs(self, logs):\n \"\"\"This test fails because logs are emitted.\n\n Output::\n\n AssertionError: In test_assert_no_logs(), the follow messages were unexpectedly logged:\n INFO:foo:first message\n ERROR:foo.bar:second message\n\n \"\"\"\n logging.getLogger('foo').info('first message')\n logging.getLogger('foo.bar').error('second message')\n\nLoggingTestCase Examples\n========================\nExample1\n--------\n\n``examples/loggingtestcase_example.py``\n\n::\n\n import unittest\n import logging\n\n import loggingtestcase\n\n\n class LoggingTestCaseExample(loggingtestcase.LoggingTestCase):\n\n def __init__(self, methodName='runTest', testlogger=None, testlevel=None):\n \"\"\"\n To change the logger or log level, override __init__.\n By default, the root logger is used and the log level is logging.INFO.\n \"\"\"\n # testlevel = logging.ERROR\n super().__init__(methodName, testlogger, testlevel)\n\n def setUp(self):\n self.logger = logging.getLogger(__name__)\n pass\n\n def test_pass(self):\n \"\"\"\n Run a test that logs an info message and\n verify the info is correctly logged.\n\n Notice that the info message is not logged to the console.\n When all your tests pass, your console output is nice and clean.\n \"\"\"\n self.logger.info(\"Starting request...\")\n self.logger.info(\"Done with request.\")\n self.assertListEqual(self.captured_logs.output,\n ['INFO:examples.loggingtestcase_example:Starting request...',\n 'INFO:examples.loggingtestcase_example:Done with request.'])\n\n def test_fail(self):\n \"\"\"\n Run a test that fails.\n\n Notice that the error message is logged to the console.\n This allows for easier debugging.\n\n Here is the output:\n ======================================================================\n ERROR: test_fail (examples.example1.Example1)\n ----------------------------------------------------------------------\n Traceback (most recent call last):\n File \"D:\\Git\\logging-test-case\\examples\\loggingtestcase_example.py.py\", line 61,\n in test_fail raise FileNotFoundError(\"Failed to open file.\")\n FileNotFoundError: Failed to open file.\n\n ERROR:examples.example1:Failed to open file.\n ----------------------------------------------------------------------\n \"\"\"\n self.logger.error(\"Failed to open file.\")\n raise FileNotFoundError(\"Failed to open file.\")\n\n\n if __name__ == \"__main__\":\n unittest.main()\n\nIn the above example, notice how ``test_pass()`` and ``test_fail()`` do\nnot have any function decorators or context managers. The captured logs\nare automatically available in ``self.captured_logs.output``.\n\nExample2 - assertNoLogs\n-----------------------\n\n``examples/assertnologs_example1.py``\n\n::\n\n import unittest\n import logging\n\n import loggingtestcase\n\n\n class AssertNoLogsExample(loggingtestcase.LoggingTestCase):\n \"\"\"Example on how to use LoggingTestCase and no logging.\"\"\"\n\n def __init__(self, methodName='runTest', testlogger=None, testlevel=None):\n \"\"\"\n To change the logger or log level, override __init__.\n By default, the root logger is used and the log level is logging.INFO.\n \"\"\"\n # testlevel = logging.ERROR\n super().__init__(methodName, testlogger, testlevel)\n\n def setUp(self):\n self.logger = logging.getLogger(__name__)\n\n def test_assert_no_logs_fail(self):\n \"\"\"The test fails because logs are emitted.\n\n Here is the output:\n E AssertionError: The follow messages were unexpectedly logged:\n E ERROR:examples.assertnologs_example1:first message\n E ERROR:examples.assertnologs_example1:second message\n\n \"\"\"\n with self.assertNoLogs():\n self.logger.error('first message')\n self.logger.error('second message')\n\n def test_assert_no_logs_pass(self):\n \"\"\"The test passes because no logs are emitted inside the context manager.\"\"\"\n self.logger.error('first message')\n with self.assertNoLogs():\n pass\n self.logger.error('second message')\n\n\n if __name__ == \"__main__\":\n unittest.main()\n\nChangelog\n=========\n\nrelease-1.4\n-----------\n* Added support for verifying no logs are emitted during a test.\n - Added method ``assertNoLogs()`` to class ``LoggingTestCase``.\n - Added parameter ``assert_no_logs`` to function decorator ``capturelogs``.\n\nrelease-1.3\n-----------\n* Support for Python 3.4, 3.5, and 3.6.\n - Previously only Python 3.6 worked.\n* Support for pytest.\n - Previously only unittest worked. Now both unittest and pytest work.\n\nThanks to jayvdb on GitHub for providing both fixes!\n\nrelease-1.2\n-----------\nFixed the following error on Python < 3.6:\n\n::\n\n /usr/local/lib/python3.5/dist-packages/loggingtestcase/capturelogs.py:31: in \n from enum import Enum, auto\n E ImportError: cannot import name 'auto'\n\nThis is because ``enum.auto()`` is new in Python 3.6. To preserve backward compatibility,\n``auto()`` is no longer used.\n\nrelease-1.1.2\n-------------\n\nAdded ``README.rst`` so this readme shows up on PyPI.\n\nrelease-1.1\n-----------\n\nAdded ``@capturelogs``.\n\nrelease-1.0\n-----------\n\nAdded ``LoggingTestCase``.\n\nTests\n=====\n\nManual Tests\n------------\n\n``tests/manual.py``\n~~~~~~~~~~~~~~~~~~~\n\nRun this file manually. All the tests are commented out. Uncomment and\nrun each test one at a time. Verify the console output.\n\nThis module is not named ``manual_test.py`` because these tests are not\nmeant to be run automatically.\n\nAutomated Tests\n---------------\n\nTo run all the tests from the command line, simply use pytest:\n\n::\n\n pytest\n\ntests/loggingtestcase\\_test.py\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThis module tests class ``LoggingTestCase``. It uses\n``subprocess.check_output`` to run each test case one at a time,\ncapturing the output. The output is examined to verify it is correct.\n``loggingtestcase_test.py`` run tests in module\n``simpleloggingtests.py``.\n\nEven though automated tests are included, it is still a good idea to run\nthe manual tests and visually look at the output of each test case.\n\ntests/assertnologs\\_test.py\n~~~~~~~~~~~~~~~~~~~~~~~~~~~\nTests context manager ``assertNoLogs`` in class ``LoggingTestCase``.\n\ntests/capturelogs\\_test.py\n~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThis module tests ``@capturelogs``, defined in\n``loggingtestcase/capturelogs.py``.\n\n\n", "description_content_type": "", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/chadrosenquist/logging-test-case", "keywords": "unit testing log files logging regression logging-test-case loggingtestcase", "license": "MIT", "maintainer": "", "maintainer_email": "", "name": "logging-test-case", "package_url": "https://pypi.org/project/logging-test-case/", "platform": "", "project_url": "https://pypi.org/project/logging-test-case/", "project_urls": { "Homepage": "https://github.com/chadrosenquist/logging-test-case" }, "release_url": "https://pypi.org/project/logging-test-case/1.4/", "requires_dist": null, "requires_python": ">=3.4", "summary": "Provides class LoggingTestCase to help test log files.", "version": "1.4" }, "last_serial": 4577939, "releases": { "1.0": [ { "comment_text": "", "digests": { "md5": "56a7b7f0b4605bdd25c0513fe5de274c", "sha256": "a6aa0f4e4051bf0f7e7957aa6817b7e7eaf03b93a112bdce9590a273a157c7cd" }, "downloads": -1, "filename": "logging_test_case-1.0-py3-none-any.whl", "has_sig": false, "md5_digest": "56a7b7f0b4605bdd25c0513fe5de274c", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 4538, "upload_time": "2017-01-07T21:47:29", "url": "https://files.pythonhosted.org/packages/7f/59/9f9cddd516f4b614a027d9275931889429d28cf430f84fdda4b4329bfd93/logging_test_case-1.0-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "3ee18548aa8730c7ba9879b57c68c42c", "sha256": "8337cb1ef5eff54575d09a3c1c96da3fcc6b435940e91c5fc36db3326629e28e" }, "downloads": -1, "filename": "logging-test-case-1.0.zip", "has_sig": false, "md5_digest": "3ee18548aa8730c7ba9879b57c68c42c", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 4654, "upload_time": "2017-01-07T21:47:31", "url": "https://files.pythonhosted.org/packages/69/c2/50c8bbbc6c6f5d27d935bd2ade253cda65fe5142b3ef01e8e7abd2bdf9a5/logging-test-case-1.0.zip" } ], "1.1": [ { "comment_text": "", "digests": { "md5": "cb0ba4f82e3a71ae2f4ce38995a8a27a", "sha256": "59e95ee47c3dac1e5eec2ade1f1ba57ffcc0ee2b335927aeb5eccc8e8d3c4a92" }, "downloads": -1, "filename": "logging_test_case-1.1-py3-none-any.whl", "has_sig": false, "md5_digest": "cb0ba4f82e3a71ae2f4ce38995a8a27a", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.6", "size": 6245, "upload_time": "2018-04-07T13:11:01", "url": "https://files.pythonhosted.org/packages/8d/ab/56377f86999e9e71def4ac68ef5e10708e378d0d1e4d40d2fc2cbfb9eb9b/logging_test_case-1.1-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "efc3b77401677560e4921fe6c4c278ec", "sha256": "7deeed363b5edee9322073f3adb14012ce13589132142726959ff6ccc43b9682" }, "downloads": -1, "filename": "logging-test-case-1.1.tar.gz", "has_sig": false, "md5_digest": "efc3b77401677560e4921fe6c4c278ec", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.6", "size": 6135, "upload_time": "2018-04-07T13:11:02", "url": "https://files.pythonhosted.org/packages/c2/e0/cf89b992864711821d9275ca91fb1af8318a39ad56f1b6d1532723f38a07/logging-test-case-1.1.tar.gz" } ], "1.1.1": [ { "comment_text": "", "digests": { "md5": "08a1ccc9f3bcb19461848ef24f61f5ff", "sha256": "cfa1996aafce27cdc7d8b6e2427f64774c7b5265a4c36c64ae92cc00a4d9e7d8" }, "downloads": -1, "filename": "logging_test_case-1.1.1-py3-none-any.whl", "has_sig": false, "md5_digest": "08a1ccc9f3bcb19461848ef24f61f5ff", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3", "size": 6270, "upload_time": "2018-04-07T20:39:11", "url": "https://files.pythonhosted.org/packages/75/b0/d1ae75ffacd0043fe9a47eaf694500923876226831bb72f5e8d0990c2975/logging_test_case-1.1.1-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "91169f1a3ae4b4d25f72fd590a6b3fd2", "sha256": "697c44a2d6248c434e2ee6e60cb3aa3f9475716f2aee6aaff6352c9c90b919c7" }, "downloads": -1, "filename": "logging-test-case-1.1.1.tar.gz", "has_sig": false, "md5_digest": "91169f1a3ae4b4d25f72fd590a6b3fd2", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3", "size": 6957, "upload_time": "2018-04-07T20:39:11", "url": "https://files.pythonhosted.org/packages/f5/b1/100d14da2014c95a4e71b8a41fb33d970988ca44aa96faf639685a51e692/logging-test-case-1.1.1.tar.gz" } ], "1.1.2": [ { "comment_text": "", "digests": { "md5": "feff841c7d69bc359c36eb72537a97cf", "sha256": "866d013d550bf64082ce1a3186a494e984a67b79565743dc43d52d991264e93c" }, "downloads": -1, "filename": "logging_test_case-1.1.2-py3-none-any.whl", "has_sig": false, "md5_digest": "feff841c7d69bc359c36eb72537a97cf", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3", "size": 8646, "upload_time": "2018-04-07T20:51:04", "url": "https://files.pythonhosted.org/packages/03/4d/e8b17723ddd4cd4d255de11457b796ec8a7bd69d5c495195d364b7245601/logging_test_case-1.1.2-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "c985ea5564b00c8f2d028aba5dea0394", "sha256": "40ca55c6d296eeee55ea9d998107055d41825e038bf20cfa01d118edc4b5d37b" }, "downloads": -1, "filename": "logging-test-case-1.1.2.tar.gz", "has_sig": false, "md5_digest": "c985ea5564b00c8f2d028aba5dea0394", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3", "size": 8121, "upload_time": "2018-04-07T20:51:05", "url": "https://files.pythonhosted.org/packages/a2/80/cf0dc6e7463425b59eebdf530c5b9b62b4708c2732a2e66ea2b01c2a2b69/logging-test-case-1.1.2.tar.gz" } ], "1.2": [ { "comment_text": "", "digests": { "md5": "3d65db7a34fc593df97ce71f8834fd5c", "sha256": "a5b968383af948cedcf2b2e6ec7672c40c0a20525df22aaa1f1c7a7799f2cc57" }, "downloads": -1, "filename": "logging_test_case-1.2-py3-none-any.whl", "has_sig": false, "md5_digest": "3d65db7a34fc593df97ce71f8834fd5c", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3", "size": 8832, "upload_time": "2018-05-03T02:19:14", "url": "https://files.pythonhosted.org/packages/5c/05/660a843a1fde4f3ae9b122a7c0109d9b7bee61b5a5750c471542c5cda9fe/logging_test_case-1.2-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "56be0d08aa514af4a2f32f94b8f9d6c3", "sha256": "6c2c39e961efbe738c38bcd7426c1aaabd19fa2e17523f5cc7a34d86b9ecb027" }, "downloads": -1, "filename": "logging-test-case-1.2.tar.gz", "has_sig": false, "md5_digest": "56be0d08aa514af4a2f32f94b8f9d6c3", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3", "size": 8395, "upload_time": "2018-05-03T02:19:15", "url": "https://files.pythonhosted.org/packages/2a/4d/c5454e47f534074f9ecc28d591115ed75bb1b66ec2156e0fb634ca420706/logging-test-case-1.2.tar.gz" } ], "1.3": [ { "comment_text": "", "digests": { "md5": "3edc67d8e40034596e5f12afc17f058d", "sha256": "3eb220596abb0a4d9c88772ae2dfd40cdf2cbde3ddb1d2dd52e30cd2503f35c2" }, "downloads": -1, "filename": "logging_test_case-1.3-py3-none-any.whl", "has_sig": false, "md5_digest": "3edc67d8e40034596e5f12afc17f058d", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.4", "size": 9118, "upload_time": "2018-05-03T03:01:30", "url": "https://files.pythonhosted.org/packages/f5/bb/96b32e032d707c08ccb96dbdceb517d33232d557d70663d73956b927dc45/logging_test_case-1.3-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "9bcd3b7b42d052025a7679917fadf728", "sha256": "3bfb604ca2f0f0f58a65d2cea3d18f697126494f8b6bccc91fac0c357b2e1196" }, "downloads": -1, "filename": "logging-test-case-1.3.tar.gz", "has_sig": false, "md5_digest": "9bcd3b7b42d052025a7679917fadf728", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.4", "size": 8736, "upload_time": "2018-05-03T03:01:31", "url": "https://files.pythonhosted.org/packages/f9/af/e99530269ba6a38011b308cfc3a78f6f1f496560b561ee0b14eb77666a80/logging-test-case-1.3.tar.gz" } ], "1.4": [ { "comment_text": "", "digests": { "md5": "9fbb9eae3f420b3dfd11c5995dce0aae", "sha256": "b06b20fd1a6c779b024a38a2745a392cc82bcd639066116cbc4acb6b7dfe7f40" }, "downloads": -1, "filename": "logging_test_case-1.4-py3-none-any.whl", "has_sig": false, "md5_digest": "9fbb9eae3f420b3dfd11c5995dce0aae", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.4", "size": 10131, "upload_time": "2018-12-09T19:11:45", "url": "https://files.pythonhosted.org/packages/5b/e1/813ff2508c76923086469afaecd5f495a910f6f844626642c5bbe4f33b9d/logging_test_case-1.4-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "5fc2672483d723d7afd97b378caa001e", "sha256": "7856574c6dfe18143349a5d43432212b43cf33bebc2aab5e2624091554da2100" }, "downloads": -1, "filename": "logging-test-case-1.4.tar.gz", "has_sig": false, "md5_digest": "5fc2672483d723d7afd97b378caa001e", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.4", "size": 8985, "upload_time": "2018-12-09T19:11:47", "url": "https://files.pythonhosted.org/packages/4c/10/588689a693a47c24467692b0bd0bb280a31efd844d3097328ff1ec76d4ae/logging-test-case-1.4.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "9fbb9eae3f420b3dfd11c5995dce0aae", "sha256": "b06b20fd1a6c779b024a38a2745a392cc82bcd639066116cbc4acb6b7dfe7f40" }, "downloads": -1, "filename": "logging_test_case-1.4-py3-none-any.whl", "has_sig": false, "md5_digest": "9fbb9eae3f420b3dfd11c5995dce0aae", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.4", "size": 10131, "upload_time": "2018-12-09T19:11:45", "url": "https://files.pythonhosted.org/packages/5b/e1/813ff2508c76923086469afaecd5f495a910f6f844626642c5bbe4f33b9d/logging_test_case-1.4-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "5fc2672483d723d7afd97b378caa001e", "sha256": "7856574c6dfe18143349a5d43432212b43cf33bebc2aab5e2624091554da2100" }, "downloads": -1, "filename": "logging-test-case-1.4.tar.gz", "has_sig": false, "md5_digest": "5fc2672483d723d7afd97b378caa001e", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.4", "size": 8985, "upload_time": "2018-12-09T19:11:47", "url": "https://files.pythonhosted.org/packages/4c/10/588689a693a47c24467692b0bd0bb280a31efd844d3097328ff1ec76d4ae/logging-test-case-1.4.tar.gz" } ] }