timacdonald/callable-fake

一个测试工具,允许您模拟和捕获可调用项/闭包的调用

v1.8.0 2024-05-18 07:32 UTC

This package is auto-updated.

Last update: 2024-08-26 22:49:23 UTC


README

Callable Fake: a PHP package by Tim MacDonald

可调用/闭包测试模拟

如果您有一个公开API允许开发者传递闭包/可调用项,但不会产生内部或外部副作用(这些留给使用接口的开发者),则此包可能有助于测试。此类添加了一些命名断言,提供了一个非常受Laravel服务模拟启发的API。它可能稍微有点冗长,但它改变了测试的语言,使其更好地反映正在发生的事情。

它还使断言调用顺序以及可调用项被调用次数变得容易。

安装

您可以使用 composer 安装此包

composer require timacdonald/callable-fake --dev

基本用法

此包需要您测试特定类型的API/交互,才能发挥作用。想象一下您正在开发一个包,它包含以下接口...

interface DependencyRepository
{
    public function each(callable $callback): void;
}

此接口接受一个回调,并在底层遍历所有“依赖项”,并将每个依赖项传递给回调,以便开发者进行处理。

之前

让我们看看对这个方法的测试可能是什么样子...

public function testEachLoopsOverAllDependencies(): void
{
    // arrange
    $received = [];
    $expected = factory(Dependency::class)->times(2)->create();
    $repo = $this->app[DependencyRepository::class];

    // act
    $repo->each(function (Dependency $dependency) use (&$received): void {
        $received[] = $dependency;
    });

    // assert
    $this->assertCount(2, $received);
    $this->assertTrue($expected[0]->is($received[0]));
    $this->assertTrue($expected[1]->is($received[1]));
}

之后

public function testEachLoopsOverAllDependencies(): void
{
    // arrange
    $callable = new CallableFake();
    $expected = factory(Dependency::class)->times(2)->create();
    $repo = $this->app[DependencyRepository::class];

    // act
    $repo->each($callable);

    // assert
    $callable->assertTimesInvoked(2);
    $callable->assertCalled(function (Depedency $dependency) use ($expected): bool {
        return $dependency->is($expected[0]);
    });
    $callable->assertCalled(function (Dependency $dependency) use ($expected): bool {
        return $dependency->is($expected[1]);
    });
}

可用的断言

所有断言都是可链的。

assertCalled(callable $callback): self

$callable->assertCalled(function (Dependency $dependency): bool {
    return Str::startsWith($dependency->name, 'spatie/');
});

assertNotCalled(callable $callback): self

$callable->assertNotCalled(function (Dependency $dependency): bool {
    return Str::startsWith($dependency->name, 'timacdonald/');
});

assertCalledIndex(callable $callback, int|array $index): self

确保可调用项以显式顺序被调用,即作为第0次和第5次调用。

$callable->assertCalledIndex(function (Dependency $dependency): bool {
    return Str::startsWith($dependency, 'spatie/');
}, [0, 5]);

assertCalledTimes(callable $callback, int $times): self

$callable->assertCalledTimes(function (Dependency $dependency): bool {
    return Str::startsWith($dependency, 'spatie/');
}, 999);

assertTimesInvoked(int $times): self

$callable->assertTimesInvoked(2);

assertInvoked(): self

$callable->assertInvoked();

assertNotInvoked(): self

$callable->assertNotInvoked();

非断言API

asClosure(): Closure

如果方法使用 \Closure 而不是可调用项进行类型提示,则可以使用此方法将可调用项转换为 \Closure 实例。

$callable = new CallableFake;

$thing->closureTypeHintedMethod($callable->asClosure());

$callable->assertInvoked();

wasInvoked(): bool

if ($callable->wasInvoked()) {
    //
}

wasNotInvoked(): bool

if ($callable->wasNotInvoked()) {
    //
}

called(callable $callback): array

$invocationArguments = $callable->called(function (Dependency $dependency): bool {
    return Str::startsWith($dependency->name, 'spatie/')
});

指定返回值

如果您需要指定返回值,这可能表明这不是正确的工具。但是,在某些情况下,返回值决定了控制流,因此它可能很有用。在这种情况下,您可以将“返回解析器”传递给命名构造函数 withReturnResolver

$callable = CallableFake::withReturnResolver(function (Dependency $dependency): bool {
    if ($dependency->version === '*') {
        return '🤠';
    }

    return '😀';
});

// You would not generally be calling this yourself, this is simply to demonstate
// what will happen under the hood...

$emoji = $callable(new Dependecy(['version' => '*']));

// $emoji === '🤠';

鸣谢

特别感谢 Caneco 为标志✨

感谢

您可以使用此包,但请与我联系,以联系某人(不是我自己)来感谢他们为您的项目中使用的开源库所做的贡献。请考虑您的整个技术堆栈:包、框架、语言、数据库、操作系统、前端、后端等。