timacdonald/log-fake

Laravel 框架测试的伪日志器。

v2.3.0 2024-05-20 03:59 UTC

This package is auto-updated.

Last update: 2024-09-05 00:34:21 UTC


README

Log Fake: a Laravel package by Tim MacDonald

Laravel 伪日志器

许多 Laravel 门面和服务可以伪造,例如使用 Bus::fake() 的调度器,以帮助测试和断言。此包允许您伪造应用程序中的日志记录器,并包括针对通道、堆栈等进行的断言的能力。

安装

您可以使用 composer 安装。

composer require timacdonald/log-fake --dev

基本用法

use Illuminate\Support\Facades\Log;
use TiMacDonald\Log\LogEntry;
use TiMacDonald\Log\LogFake;

public function testItLogsWhenAUserAuthenticates()
{
    /*
     * Test setup.
     *
     * In the setup of your tests, you can call the following `bind` helper,
     * which will switch out the underlying log driver with the fake.
     */
    LogFake::bind();

    /*
     * Application implementation.
     *
     * In your application's implementation, you then utilise the logger, as you
     * normally would.
     */
    Log::info('User logged in.', ['user_id' => $user->id]);

    /*
     * Test assertions.
     *
     * Finally you can make assertions against the log channels, stacks, etc. to
     * ensure the expected logging occurred in your implementation.
     */
    Log::assertLogged(fn (LogEntry $log) =>
        $log->level === 'info'
        && $log->message === 'User logged in.' 
        && $log->context === ['user_id' => 5]
    );
}

通道

如果您在应用程序中向特定通道(即非默认通道)记录日志,则需要以相同方式对断言进行前缀。

public function testItLogsWhenAUserAuthenticates()
{
    // setup...
    LogFake::bind();

    // implementation...
    Log::channel('slack')->info('User logged in.', ['user_id' => $user->id]);

    // assertions...
    Log::channel('slack')->assertLogged(
        fn (LogEntry $log) => $log->message === 'User logged in.'
    );
}

堆栈

如果您在应用程序中向堆栈记录日志,就像通道一样,您需要为断言添加前缀。请注意,堆栈的顺序无关紧要。

public function testItLogsWhenAUserAuthenticates()
{
    // setup...
    LogFake::bind();

    // implementation...
    Log::stack(['stderr', 'single'])->info('User logged in.', ['user_id' => $user->id]);

    // assertions...
    Log::stack(['stderr', 'single'])->assertLogged(
        fn (LogEntry $log) => $log->message === 'User logged in.'
    );
}

就是这样。现在让我们深入了解可用的断言,以提高您测试应用程序日志记录的体验。

可用的断言

请记住,所有断言都与上面显示的通道或堆栈相关。

assertLogged()

断言已创建日志。

可以在以下位置调用

  • 门面基础(默认通道)
  • 通道
  • 堆栈

示例测试

/*
 * implementation...
 */

Log::info('User logged in.');

/*
 * assertions...
 */

Log::assertLogged(
    fn (LogEntry $log) => $log->message === 'User logged in.'
); // ✅

Log::assertLogged(
    fn (LogEntry $log) => $log->level === 'critical'
); // ❌ as log had a level of `info`.

assertLoggedTimes()

断言日志创建特定次数。

可以在以下位置调用

  • 门面基础(默认通道)
  • 通道
  • 堆栈

示例测试

/*
 * implementation...
 */

Log::info('Stripe request initiated.');

Log::info('Stripe request initiated.');

/*
 * assertions...
 */

Log::assertLoggedTimes(
    fn (LogEntry $log) => $log->message === 'Stripe request initiated.',
    2
); // ✅

Log::assertLoggedTimes(
    fn (LogEntry $log) => $log->message === 'Stripe request initiated.',
    99
); // ❌ as the log was created twice, not 99 times.

assertNotLogged()

断言日志从未创建。

可以在以下位置调用

  • 门面基础(默认通道)
  • 通道
  • 堆栈

示例测试

/*
 * implementation...
 */

Log::info('User logged in.');

/*
 * assertions...
 */

Log::assertNotLogged(
    fn (LogEntry $log) => $log->level === 'critical'
); // ✅

Log::assertNotLogged(
    fn (LogEntry $log) => $log->level === 'info'
); // ❌ as the level was `info`.

assertNothingLogged()

断言未创建任何日志。

可以在以下位置调用

  • 门面基础(默认通道)
  • 通道
  • 堆栈

示例测试

/*
 * implementation...
 */

Log::channel('single')->info('User logged in.');

/*
 * assertions...
 */

Log::channel('stderr')->assertNothingLogged(); // ✅

Log::channel('single')->assertNothingLogged(); // ❌ as a log was created in the `single` channel.

assertWasForgotten()

断言通道至少被遗忘一次。

可以在以下位置调用

  • 门面基础(默认通道)
  • 通道
  • 堆栈

示例测试

/*
 * implementation...
 */

Log::channel('single')->info('User logged in.');

Log::forgetChannel('single');

/*
 * assertions...
 */

Log::channel('single')->assertWasForgotten(); // ✅

Log::channel('stderr')->assertWasForgotten(); // ❌ as it was the `single` not the `stderr` channel that was not forgotten.

assertWasForgottenTimes()

断言通道被遗忘特定次数。

可以在以下位置调用

  • 门面基础(默认通道)
  • 通道
  • 堆栈

示例测试

/*
 * implementation...
 */

Log::channel('single')->info('User logged in.');

Log::forgetChannel('single');

Log::channel('single')->info('User logged in.');

Log::forgetChannel('single');

/*
 * assertions...
 */

Log::channel('single')->assertWasForgottenTimes(2); // ✅

Log::channel('single')->assertWasForgottenTimes(99); // ❌ as the channel was forgotten twice, not 99 times.

assertWasNotForgotten()

断言通道未被遗忘。

可以在以下位置调用

  • 门面基础(默认通道)
  • 通道
  • 堆栈

示例测试

/*
 * implementation...
 */

Log::channel('single')->info('User logged in.');

/*
 * assertions...
 */

Log::channel('single')->assertWasNotForgotten(); // ✅

assertChannelIsCurrentlyForgotten()

断言通道目前已被遗忘。这与断言通道已经被遗忘不同。

可以在以下位置调用

  • 门面基础 (默认通道)
  • 通道
  • 堆栈

示例测试

/*
 * implementation...
 */

Log::channel('single')->info('xxxx');

Log::forgetChannel('single');

/*
 * assertions...
 */

Log::assertChannelIsCurrentlyForgotten('single'); // ✅

Log::assertChannelIsCurrentlyForgotten('stderr'); // ❌ as the `single` channel was forgotten, not the `stderr` channel.

assertCurrentContext()

断言通道目前具有指定的上下文。您可以将预期的上下文作为数组提供,或者您可以提供真值测试闭包以检查当前上下文。

可以在以下位置调用

  • 门面基础(默认通道)
  • 通道
  • 堆栈

示例测试

/*
 * implementation...
 */

Log::withContext([
    'app' => 'Acme CRM',
]);

Log::withContext([
    'env' => 'production',
]);

/*
 * assertions...
 */

Log::assertCurrentContext([
    'app' => 'Acme CRM',
    'env' => 'production',
]); // ✅

Log::assertCurrentContext(
    fn (array $context) => $context['app'] === 'Acme CRM')
); // ✅

Log::assertCurrentContext([
    'env' => 'production',
]); // ❌ missing the "app" key.

Log::assertCurrentContext(
    fn (array $context) => $context['env'] === 'develop')
); // ❌ the 'env' key is set to "production"

assertHasSharedContext()

断言日志管理器目前具有给定的共享上下文。您可以将预期的上下文作为数组提供,或者您可以提供真值测试闭包以检查当前上下文。

可以在以下位置调用

  • 门面基础(默认通道)
  • 通道
  • 堆栈

示例测试

/*
 * implementation...
 */

Log::shareContext([
    'invocation-id' => '54',
]);

/*
 * assertions...
 */

Log::assertHasSharedContext([
    'invocation-id' => '54',
]); // ✅

Log::assertCurrentContext(
    fn (array $context) => $context['invocation-id'] === '54')
); // ✅

Log::assertCurrentContext([
    'invocation-id' => '99',
]); // ❌ wrong invocation ID

Log::assertCurrentContext(
    fn (array $context) => $context['invocation-id'] === '99')
); // ❌ wrong invocation ID

检查

有时在调试测试时,查看已记录的消息很有用。有几个助手可以帮助完成此操作。

dump()

导出通道中的所有日志。您还可以传递基于真值的闭包以过滤要导出的日志。

可以在以下位置调用

  • 门面基础(默认通道)
  • 通道
  • 堆栈
/*
 * implementation...
 */

Log::info('User logged in.');

Log::channel('slack')->alert('Stripe request initiated.');

/*
 * inspection...
 */

Log::dump();

// array:1 [
//   0 => array:4 [
//     "level" => "info"
//     "message" => "User logged in."
//     "context" => []
//     "channel" => "stack"
//   ]
// ]

Log::channel('slack')->dump();

// array:1 [
//   0 => array:4 [
//     "level" => "alert"
//     "message" => "Stripe request initiated."
//     "context" => []
//     "channel" => "slack"
//   ]
// ]

dd()

dump相同,但还会结束执行。

dumpAll()

导出所有通道的日志。也接受真值测试闭包以过滤任何日志。

可以在以下位置调用

  • 门面基础 (默认通道)
  • 通道
  • 堆栈

示例用法

/*
 * implementation...
 */

Log::info('User logged in.');

Log::channel('slack')->alert('Stripe request initiated.');

/*
 * inspection...
 */

Log::dumpAll();

// array:2 [
//   0 => array:4 [
//     "level" => "info"
//     "message" => "User logged in."
//     "context" => []
//     "times_channel_has_been_forgotten_at_time_of_writing_log" => 0
//     "channel" => "stack"
//   ]
//   1 => array:4 [
//     "level" => "alert"
//     "message" => "Stripe request initiated."
//     "context" => []
//     "times_channel_has_been_forgotten_at_time_of_writing_log" => 0
//     "channel" => "slack"
//   ]
// ]

ddAll()

dumpAll()相同,但也会结束执行。

其他 API

logs()

从通道或堆栈获取所有日志条目的集合。

可以在以下位置调用

  • 门面基础(默认通道)
  • 通道
  • 堆栈

示例用法

/*
 * implementation...
 */

Log::channel('slack')->info('User logged in.');

Log::channel('slack')->alert('Stripe request initiated.');

/*
 * example usage...
 */

$logs = Log::channel('slack')->logs();

assert($logs->count() === 2); ✅

allLogs()

logs()类似,但它在Facade基类上被调用,并返回来自所有通道和堆栈的日志集合。

可以在以下位置调用

  • 门面基础 (默认通道)
  • 通道
  • 堆栈

示例用法

/*
 * implementation...
 */

Log::info('User logged in.');

Log::channel('slack')->alert('Stripe request initiated.');

/*
 * example usage...
 */

$logs = Log::allLogs();

assert($logs->count() === 2); ✅

致谢

并特别感谢Caneco为标志✨所做的贡献

感谢

您可以自由使用此包,但我要求您联系某人(不是我自己),这个人曾经或目前正在维护或为您的项目中的开源库做出贡献,并对他们的工作表示感谢。考虑您的整个技术栈:包、框架、语言、数据库、操作系统、前端、后端等。