From 2302dbc901a8049ecedcbf8ffa79dadba0fa957a Mon Sep 17 00:00:00 2001 From: "John Paul E. Balandan, CPA" Date: Tue, 18 Oct 2022 19:43:35 +0800 Subject: [PATCH 1/2] feat: Check logs against parts of the message only --- system/Test/CIUnitTestCase.php | 17 ++++++- system/Test/TestLogger.php | 18 ++++++- tests/system/Test/TestCaseTest.php | 6 +++ tests/system/Test/TestLoggerTest.php | 71 ++++++++++++++++++++++++++++ 4 files changed, 108 insertions(+), 4 deletions(-) create mode 100644 tests/system/Test/TestLoggerTest.php diff --git a/system/Test/CIUnitTestCase.php b/system/Test/CIUnitTestCase.php index 47fb6dc92e58..92444ff7fc88 100644 --- a/system/Test/CIUnitTestCase.php +++ b/system/Test/CIUnitTestCase.php @@ -349,8 +349,6 @@ protected function mockSession() * @param string|null $expectedMessage * * @return bool - * - * @throws Exception */ public function assertLogged(string $level, $expectedMessage = null) { @@ -365,6 +363,21 @@ public function assertLogged(string $level, $expectedMessage = null) return $result; } + /** + * Asserts that there is a log record that contains `$logMessage` in the message. + */ + public function assertLogContains(string $level, string $logMessage, string $message = ''): void + { + $this->assertTrue( + TestLogger::didLog($level, $logMessage, false), + $message ?: sprintf( + 'Failed asserting that logs have a record of message containing "%s" with level "%s".', + $logMessage, + $level + ) + ); + } + /** * Hooks into CodeIgniter's Events system to check if a specific * event was triggered or not. diff --git a/system/Test/TestLogger.php b/system/Test/TestLogger.php index 68e45674eb0e..989fb6ef5c13 100644 --- a/system/Test/TestLogger.php +++ b/system/Test/TestLogger.php @@ -59,10 +59,24 @@ public function log($level, $message, array $context = []): bool * * @return bool */ - public static function didLog(string $level, $message) + public static function didLog(string $level, $message, bool $useExactComparison = true) { + $lowerLevel = strtolower($level); + foreach (self::$op_logs as $log) { - if (strtolower($log['level']) === strtolower($level) && $message === $log['message']) { + if (strtolower($log['level']) !== $lowerLevel) { + continue; + } + + if ($useExactComparison) { + if ($log['message'] === $message) { + return true; + } + + continue; + } + + if (strpos($log['message'], $message) !== false) { return true; } } diff --git a/tests/system/Test/TestCaseTest.php b/tests/system/Test/TestCaseTest.php index a82a63773840..f91baf25b435 100644 --- a/tests/system/Test/TestCaseTest.php +++ b/tests/system/Test/TestCaseTest.php @@ -45,6 +45,12 @@ public function testLogging() $this->assertLogged('error', 'Some variable did not contain a value.'); } + public function testAssertLogContains() + { + log_message('error', 'Some variable did not contain a value.'); + $this->assertLogContains('error', 'variable did not'); + } + public function testEventTriggering() { Events::on('foo', static function ($arg) use (&$result) { diff --git a/tests/system/Test/TestLoggerTest.php b/tests/system/Test/TestLoggerTest.php new file mode 100644 index 000000000000..96fd255563bd --- /dev/null +++ b/tests/system/Test/TestLoggerTest.php @@ -0,0 +1,71 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace CodeIgniter\Test; + +use Config\Logger; + +/** + * @internal + */ +final class TestLoggerTest extends CIUnitTestCase +{ + /** + * @dataProvider provideDidLogCases + */ + public function testDidLogMethod(bool $expected, string $level, string $message, bool $exact): void + { + (new TestLogger(new Logger()))->log('error', 'Some variable did not contain a value.'); + + $this->assertSame( + $expected, + TestLogger::didLog($level, $message, $exact), + ); + } + + public function provideDidLogCases(): iterable + { + yield 'exact' => [ + true, + 'error', + 'Some variable did not contain a value.', + true, + ]; + + yield 'wrong level' => [ + false, + 'warning', + 'Some variable did not contain a value.', + true, + ]; + + yield 'wrong message' => [ + false, + 'error', + 'Some variables did not contain a value.', + true, + ]; + + yield 'approximate' => [ + true, + 'error', + 'Some variable did not', + false, + ]; + + yield 'approximate but wrong level' => [ + false, + 'warning', + 'Some variable did not', + false, + ]; + } +} From 489478825e46ac32e20103fe71dade1fca71a560 Mon Sep 17 00:00:00 2001 From: "John Paul E. Balandan, CPA" Date: Tue, 18 Oct 2022 19:58:27 +0800 Subject: [PATCH 2/2] docs: Add to user guide and changelog --- user_guide_src/source/changelogs/v4.3.0.rst | 2 ++ user_guide_src/source/testing/overview.rst | 7 ++++++- user_guide_src/source/testing/overview/007.php | 12 ++++++++---- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/user_guide_src/source/changelogs/v4.3.0.rst b/user_guide_src/source/changelogs/v4.3.0.rst index ea83444738af..0377c19dd34e 100644 --- a/user_guide_src/source/changelogs/v4.3.0.rst +++ b/user_guide_src/source/changelogs/v4.3.0.rst @@ -128,6 +128,8 @@ Testing - The CITestStreamFilter filter class now implements methods for adding a filter to streams. See :ref:`testing-cli-output`. - Added the ``PhpStreamWrapper`` to make it easier to work with setting data to ``php://stdin``. See :ref:`testing-cli-input`. - Added method :ref:`benchmark-timer-record` to measure performance in a callable. Also enhanced common function ``timer()`` to accept optional callable. +- A boolean third parameter ``$useExactComparison`` is added to ``TestLogger::didLog()`` which sets whether log messages are checked verbatim. This defaults to ``true``. +- Added method ``CIUnitTestCase::assertLogContains()`` which compares log messages by parts instead of the whole of the message. Database ======== diff --git a/user_guide_src/source/testing/overview.rst b/user_guide_src/source/testing/overview.rst index 1c7ea0f75c39..41ececd1b2d4 100644 --- a/user_guide_src/source/testing/overview.rst +++ b/user_guide_src/source/testing/overview.rst @@ -126,7 +126,12 @@ Additional Assertions assertLogged($level, $expectedMessage) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Ensure that something you expected to be logged actually was: +Ensure that something you expected to be logged was actually logged: + +assertLogContains($level, $logMessage) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Ensure that there's a record in the logs which contains a message part. .. literalinclude:: overview/007.php diff --git a/user_guide_src/source/testing/overview/007.php b/user_guide_src/source/testing/overview/007.php index 55077ed36818..2d48511a046a 100644 --- a/user_guide_src/source/testing/overview/007.php +++ b/user_guide_src/source/testing/overview/007.php @@ -1,9 +1,13 @@ log('error', "That's no moon"); - $this->assertLogged('error', "That's no moon"); + +// check that a portion of the message is found in the logs +$exception = new RuntimeException('Hello world.'); +$logger->log('error', $exception->getTraceAsString()); +$this->assertLogContains('error', '{main}');