Skip to content

Instantly share code, notes, and snippets.

@bizley
Last active April 3, 2024 07:35
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bizley/d4bd39d0c4a1b4106c56845ff0c07068 to your computer and use it in GitHub Desktop.
Save bizley/d4bd39d0c4a1b4106c56845ff0c07068 to your computer and use it in GitHub Desktop.
PHPUnit 10 withConsecutive() replacement (idea by nicolas-grekas)
<?php
declare(strict_types=1);
namespace App\Tests\Unit;
use PHPUnit\Framework\Assert;
use PHPUnit\Framework\Constraint\Constraint;
class ConsecutiveCalls extends Assert
{
/**
* @var array<mixed[]>
*/
private array $data = [];
private int $internalCounter = -1;
/**
* @param mixed[] ...$args
*/
private function __construct(array ...$args)
{
foreach ($args as $arg) {
if (!\is_array($arg)) {
throw new \InvalidArgumentException('All arguments must be arrays');
}
$this->data[] = $arg;
}
}
/**
* @param mixed[] ...$arguments
*/
public static function withArgs(array ...$arguments): self
{
return new self(...$arguments);
}
public function __invoke(mixed ...$args): void
{
$testData = $this->data[++$this->internalCounter] ?? null;
if ($testData === null) {
$testData = $this->data[$this->internalCounter % \count($this->data)];
}
foreach ($testData as $key => $value) {
if ($value instanceof Constraint) {
$value->evaluate($args[$key]);
} else {
self::assertEquals($value, $args[$key]);
}
}
}
}
<?php
declare(strict_types=1);
namespace App\Tests\Unit;
use PHPUnit\Framework\TestCase;
class BearerTest extends TestCase
{
public function testConsecutiveArguments(): void
{
// expects arguments:
// 'abc' and 8 on first call
// 'def' and 2 on second call
// will return 1 every time
$this->createMock(Mocked::class)
->expects(self::exactly(2))
->method('name')->willReturnCallback(
ConsecutiveCalls::withArgs(
['abc', 8],
['def', 2],
)
)
->willReturn(1);
// expects arguments:
// 'abc' and 8 on first and on second call
// will return 1 on first call, and 2 on second call
$this->createMock(Mocked::class)
->expects(self::exactly(2))
->method('name')->willReturnCallback(
ConsecutiveCalls::withArgs(
['abc', 8]
)
)
->willReturnOnConsecutiveCalls(1, 2);
// expects arguments:
// 'abc' and object fullfilling the callback
$this->createMock(Mocked::class)
->expects(self::once())
->method('name')->willReturnCallback(
ConsecutiveCalls::withArgs(
['abc', self::callback(
static fn (Abc $arg) => $arg->getStatus() === 1
)]
)
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment