From cf243baeda209cc70b893b6015f11ff3e3d0f54e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Re=CC=81mi=20Pelhate?= Date: Fri, 5 Jun 2026 11:56:44 +0200 Subject: [PATCH 1/5] Setup WithSameArguments assertion for WasCallable --- .../Assertions/WithSameArguments.php | 29 ++++++++ .../Assertions/WithSameArgumentsTest.php | 68 +++++++++++++++++++ 2 files changed, 97 insertions(+) create mode 100644 src/PHPUnit/Constraint/Callables/Assertions/WithSameArguments.php create mode 100644 src/PHPUnit/Constraint/Callables/Assertions/WithSameArgumentsTest.php diff --git a/src/PHPUnit/Constraint/Callables/Assertions/WithSameArguments.php b/src/PHPUnit/Constraint/Callables/Assertions/WithSameArguments.php new file mode 100644 index 0000000..0a5b5d8 --- /dev/null +++ b/src/PHPUnit/Constraint/Callables/Assertions/WithSameArguments.php @@ -0,0 +1,29 @@ +expected = $expected; + } + + public function __invoke(mixed ...$actual): void + { + Assert::assertCount(count($this->expected), $actual); + + foreach ($actual as $key => $value) { + Assert::assertSame($this->expected[$key], $value); + } + } +} diff --git a/src/PHPUnit/Constraint/Callables/Assertions/WithSameArgumentsTest.php b/src/PHPUnit/Constraint/Callables/Assertions/WithSameArgumentsTest.php new file mode 100644 index 0000000..2b18f84 --- /dev/null +++ b/src/PHPUnit/Constraint/Callables/Assertions/WithSameArgumentsTest.php @@ -0,0 +1,68 @@ +expectException(ExpectationFailedException::class); + + $instance->__invoke(...$actual); + } + + #[Test] + #[TestWith(['first different', 'last'])] + #[TestWith(['first', 'last different'])] + public function itFailsWhenInvokedWithDifferentArguments(string ...$actual): void + { + $instance = new WithSameArguments('first', 'last'); + + $this->expectException(ExpectationFailedException::class); + + $instance->__invoke(...$actual); + } + + #[Test] + public function itFailsWhenInvokedWithArgumentsInDifferentOrder(): void + { + $instance = new WithSameArguments('first', 'last'); + + $this->expectException(ExpectationFailedException::class); + + $instance->__invoke('last', 'first'); + } + + #[Test] + public function itFailsWhenInvokedWithNonIdenticalArguments(): void + { + $instance = new WithSameArguments(new stdClass()); + + $this->expectException(ExpectationFailedException::class); + + $instance->__invoke(new stdClass()); + } + + #[Test] + #[TestWith(['first', 'second', 'last'], 'Scalar arguments')] + #[TestWith([new stdClass()], 'Object arguments')] + public function itPassesWhenInvokedWithSameArguments(mixed ...$actual): void + { + $instance = new WithSameArguments(...$actual); + + $instance->__invoke(...$actual); + } +} From 54a791dd7484ec95539ea7864d22d2cd61c079cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Re=CC=81mi=20Pelhate?= Date: Fri, 5 Jun 2026 12:05:00 +0200 Subject: [PATCH 2/5] Remove WasCalled::withSame() in favour of WithSameArguments --- src/Laravel/Constraint/Bus/WasHandledTest.php | 17 ++++------- .../Constraint/Callables/WasCalled.php | 28 ++++++++----------- .../DataProviders/QuantableConstraintTest.php | 7 ++--- 3 files changed, 19 insertions(+), 33 deletions(-) diff --git a/src/Laravel/Constraint/Bus/WasHandledTest.php b/src/Laravel/Constraint/Bus/WasHandledTest.php index 372d60b..837f794 100644 --- a/src/Laravel/Constraint/Bus/WasHandledTest.php +++ b/src/Laravel/Constraint/Bus/WasHandledTest.php @@ -4,6 +4,7 @@ namespace Craftzing\TestBench\Laravel\Constraint\Bus; +use Craftzing\TestBench\PHPUnit\Constraint\Callables\Assertions\WithSameArguments; use Craftzing\TestBench\PHPUnit\Constraint\Callables\WasCalled; use Craftzing\TestBench\PHPUnit\Constraint\Objects\DeriveConstraintsFromObjectUsingFakes; use Craftzing\TestBench\PHPUnit\Constraint\Objects\DeriveConstraintsFromObjectUsingReflection; @@ -313,18 +314,10 @@ public function itDerivesConstraintsFromExpectedCommandsAndMatchesItAgainstActua $this->assertThat($expected, new WasHandled()); - $deriveConstraints->invoke->assert(new WasCalled()->withSame($expected)); - $deriveConstraints->invoke->assert( - new WasCalled() - ->never() - ->withSame($actual), - ); - $constraint->matches->assert(new WasCalled()->withSame($actual)); - $constraint->matches->assert( - new WasCalled() - ->never() - ->withSame($expected), - ); + $deriveConstraints->invoke->assert(new WasCalled(new WithSameArguments($expected))); + $deriveConstraints->invoke->assert(new WasCalled(new WithSameArguments($expected))->never()); + $constraint->matches->assert(new WasCalled(new WithSameArguments($expected))); + $constraint->matches->assert(new WasCalled(new WithSameArguments($expected))->never()); } private function command(array $properties): stdClass diff --git a/src/PHPUnit/Constraint/Callables/WasCalled.php b/src/PHPUnit/Constraint/Callables/WasCalled.php index 37751de..2c0bf66 100644 --- a/src/PHPUnit/Constraint/Callables/WasCalled.php +++ b/src/PHPUnit/Constraint/Callables/WasCalled.php @@ -4,38 +4,31 @@ namespace Craftzing\TestBench\PHPUnit\Constraint\Callables; -use Closure; use Craftzing\TestBench\PHPUnit\Constraint\ProvidesAdditionalFailureDescription; use Craftzing\TestBench\PHPUnit\Constraint\Quantable; use Craftzing\TestBench\PHPUnit\Doubles\CallableInvocation; use Craftzing\TestBench\PHPUnit\Doubles\SpyCallable; use InvalidArgumentException; use Override; -use PHPUnit\Framework\Assert; use PHPUnit\Framework\Constraint\Constraint; use PHPUnit\Framework\ExpectationFailedException; use function array_filter; use function count; +use function is_callable; final class WasCalled extends Constraint implements Quantable { use ProvidesAdditionalFailureDescription; + /** @var callable|null */ + public readonly mixed $assertInvocation; + public function __construct( - public readonly ?Closure $assertInvocation = null, + ?callable $assertInvocation = null, public readonly ?int $times = null, - ) {} - - public function withSame(mixed ...$expected): self - { - return new self(static function (mixed ...$actual) use ($expected): void { - Assert::assertCount(count($expected), $actual); - - foreach ($actual as $key => $value) { - Assert::assertSame($expected[$key], $value); - } - }, $this->times); + ) { + $this->assertInvocation = $assertInvocation; } public function times(int $count): self @@ -72,9 +65,12 @@ protected function matches(mixed $other): bool private function matchesInvocationAssertions(CallableInvocation $invocation): bool { + if (!is_callable($this->assertInvocation)) { + return false; + } + try { - // @mago-expect analyzer:invalid-method-access - $this->assertInvocation?->__invoke(...$invocation->arguments); + ( $this->assertInvocation )(...$invocation->arguments); } catch (ExpectationFailedException $expectationFailed) { $this->additionalFailureDescriptions[] = $expectationFailed->getMessage(); diff --git a/src/PHPUnit/DataProviders/QuantableConstraintTest.php b/src/PHPUnit/DataProviders/QuantableConstraintTest.php index 4b78a46..d97d9a4 100644 --- a/src/PHPUnit/DataProviders/QuantableConstraintTest.php +++ b/src/PHPUnit/DataProviders/QuantableConstraintTest.php @@ -4,6 +4,7 @@ namespace Craftzing\TestBench\PHPUnit\DataProviders; +use Craftzing\TestBench\PHPUnit\Constraint\Callables\Assertions\WithSameArguments; use Craftzing\TestBench\PHPUnit\Constraint\Callables\WasCalled; use Craftzing\TestBench\PHPUnit\Constraint\Quantable; use Craftzing\TestBench\PHPUnit\Doubles\SpyCallable; @@ -63,11 +64,7 @@ public function itCanBeInvokedOnConstraints(string $method, int $times): void $instance($constraint); - $constraint->spy->assert( - new WasCalled() - ->withSame($instance->method, $instance->times) - ->once(), - ); + $constraint->spy->assert(new WasCalled(new WithSameArguments($constraint))->once()); } #[Test] From 2ded483e3eab6a247a92e0c4d109b65d3cba9df7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Re=CC=81mi=20Pelhate?= Date: Fri, 5 Jun 2026 12:08:42 +0200 Subject: [PATCH 3/5] Add WithEqualArguments assertion --- .../Assertions/WithEqualArguments.php | 29 ++++++++ .../Assertions/WithEqualArgumentsTest.php | 66 +++++++++++++++++++ 2 files changed, 95 insertions(+) create mode 100644 src/PHPUnit/Constraint/Callables/Assertions/WithEqualArguments.php create mode 100644 src/PHPUnit/Constraint/Callables/Assertions/WithEqualArgumentsTest.php diff --git a/src/PHPUnit/Constraint/Callables/Assertions/WithEqualArguments.php b/src/PHPUnit/Constraint/Callables/Assertions/WithEqualArguments.php new file mode 100644 index 0000000..4e70ef6 --- /dev/null +++ b/src/PHPUnit/Constraint/Callables/Assertions/WithEqualArguments.php @@ -0,0 +1,29 @@ +expected = $expected; + } + + public function __invoke(mixed ...$actual): void + { + Assert::assertCount(count($this->expected), $actual); + + foreach ($actual as $key => $value) { + Assert::assertEquals($this->expected[$key], $value); + } + } +} diff --git a/src/PHPUnit/Constraint/Callables/Assertions/WithEqualArgumentsTest.php b/src/PHPUnit/Constraint/Callables/Assertions/WithEqualArgumentsTest.php new file mode 100644 index 0000000..3c92d1d --- /dev/null +++ b/src/PHPUnit/Constraint/Callables/Assertions/WithEqualArgumentsTest.php @@ -0,0 +1,66 @@ +expectException(ExpectationFailedException::class); + + $instance->__invoke(...$actual); + } + + #[Test] + #[TestWith(['first different', 'last'])] + #[TestWith(['first', 'last different'])] + public function itFailsWhenInvokedWithDifferentArguments(string ...$actual): void + { + $instance = new WithEqualArguments('first', 'last'); + + $this->expectException(ExpectationFailedException::class); + + $instance->__invoke(...$actual); + } + + #[Test] + public function itFailsWhenInvokedWithArgumentsInDifferentOrder(): void + { + $instance = new WithEqualArguments('first', 'last'); + + $this->expectException(ExpectationFailedException::class); + + $instance->__invoke('last', 'first'); + } + + #[Test] + #[TestWith(['first', 'second', 'last'], 'Scalar arguments')] + #[TestWith([new stdClass()], 'Object arguments')] + public function itPassesWhenInvokedWithSameArguments(mixed ...$actual): void + { + $instance = new WithEqualArguments(...$actual); + + $instance->__invoke(...$actual); + } + + #[Test] + public function itPassesWhenInvokedWithEqualArguments(): void + { + $instance = new WithEqualArguments(new stdClass()); + + $instance->__invoke(new stdClass()); + } +} From d77d209d3b25ac369fafabc0b4dabe24743a269d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Re=CC=81mi=20Pelhate?= Date: Fri, 5 Jun 2026 13:55:14 +0200 Subject: [PATCH 4/5] Fix tests --- src/Laravel/Constraint/Bus/WasHandledTest.php | 4 +-- .../Constraint/Callables/WasCalled.php | 26 +++++++++---------- .../Constraint/Callables/WasCalledTest.php | 12 ++++----- .../DataProviders/QuantableConstraintTest.php | 2 +- 4 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/Laravel/Constraint/Bus/WasHandledTest.php b/src/Laravel/Constraint/Bus/WasHandledTest.php index 837f794..baf9715 100644 --- a/src/Laravel/Constraint/Bus/WasHandledTest.php +++ b/src/Laravel/Constraint/Bus/WasHandledTest.php @@ -315,8 +315,8 @@ public function itDerivesConstraintsFromExpectedCommandsAndMatchesItAgainstActua $this->assertThat($expected, new WasHandled()); $deriveConstraints->invoke->assert(new WasCalled(new WithSameArguments($expected))); - $deriveConstraints->invoke->assert(new WasCalled(new WithSameArguments($expected))->never()); - $constraint->matches->assert(new WasCalled(new WithSameArguments($expected))); + $deriveConstraints->invoke->assert(new WasCalled(new WithSameArguments($actual))->never()); + $constraint->matches->assert(new WasCalled(new WithSameArguments($actual))); $constraint->matches->assert(new WasCalled(new WithSameArguments($expected))->never()); } diff --git a/src/PHPUnit/Constraint/Callables/WasCalled.php b/src/PHPUnit/Constraint/Callables/WasCalled.php index 2c0bf66..148cba9 100644 --- a/src/PHPUnit/Constraint/Callables/WasCalled.php +++ b/src/PHPUnit/Constraint/Callables/WasCalled.php @@ -22,28 +22,28 @@ final class WasCalled extends Constraint implements Quantable use ProvidesAdditionalFailureDescription; /** @var callable|null */ - public readonly mixed $assertInvocation; + public readonly mixed $withArguments; public function __construct( - ?callable $assertInvocation = null, + ?callable $withArguments = null, public readonly ?int $times = null, ) { - $this->assertInvocation = $assertInvocation; + $this->withArguments = $withArguments; } public function times(int $count): self { - return new self($this->assertInvocation, $count); + return new self($this->withArguments, $count); } public function never(): self { - return new self($this->assertInvocation, 0); + return new self($this->withArguments, 0); } public function once(): self { - return new self($this->assertInvocation, 1); + return new self($this->withArguments, 1); } #[Override] @@ -55,7 +55,7 @@ protected function matches(mixed $other): bool ); } - $matchingInvocations = array_filter($other->invocations, $this->matchesInvocationAssertions(...)); + $matchingInvocations = array_filter($other->invocations, $this->wasInvokedWithExpectedArguments(...)); return match ($this->times) { null => $matchingInvocations !== [], @@ -63,14 +63,14 @@ protected function matches(mixed $other): bool }; } - private function matchesInvocationAssertions(CallableInvocation $invocation): bool + private function wasInvokedWithExpectedArguments(CallableInvocation $invocation): bool { - if (!is_callable($this->assertInvocation)) { - return false; + if (!is_callable($this->withArguments)) { + return true; } try { - ( $this->assertInvocation )(...$invocation->arguments); + ( $this->withArguments )(...$invocation->arguments); } catch (ExpectationFailedException $expectationFailed) { $this->additionalFailureDescriptions[] = $expectationFailed->getMessage(); @@ -88,8 +88,8 @@ public function toString(): string $message .= " {$this->times} time(s)"; } - if ($this->assertInvocation !== null) { - $message .= ' with given invocation assertions'; + if ($this->withArguments !== null) { + $message .= ' with given arguments'; } return $message; diff --git a/src/PHPUnit/Constraint/Callables/WasCalledTest.php b/src/PHPUnit/Constraint/Callables/WasCalledTest.php index c31a1c9..bafb1d8 100644 --- a/src/PHPUnit/Constraint/Callables/WasCalledTest.php +++ b/src/PHPUnit/Constraint/Callables/WasCalledTest.php @@ -22,7 +22,7 @@ public function itCanConstructWithoutArguments(): void { $instance = new WasCalled(); - $this->assertNull($instance->assertInvocation); + $this->assertNull($instance->withArguments); $this->assertNull($instance->times); } @@ -33,7 +33,7 @@ public function itCanConstructWithInvocationAssertions(): void $instance = new WasCalled($assertInvocation); - $this->assertSame($assertInvocation, $instance->assertInvocation); + $this->assertSame($assertInvocation, $instance->withArguments); $this->assertNull($instance->times); } @@ -47,9 +47,9 @@ public function itImplementsTheQuantableInterface(QuantableConstraint $quantise) $quantisedInstance = $quantise($instance); $this->assertNull($instance->times); - $this->assertSame($assertInvocation, $instance->assertInvocation); + $this->assertSame($assertInvocation, $instance->withArguments); $this->assertSame($quantise->times, $quantisedInstance->times); - $this->assertSame($assertInvocation, $quantisedInstance->assertInvocation); + $this->assertSame($assertInvocation, $quantisedInstance->withArguments); } #[Test] @@ -87,7 +87,7 @@ public function itFailsWhenNotCalledWithExpectedArguments(): void $callable('last', 'first'); $this->expectException(ExpectationFailedException::class); - $this->expectExceptionMessage('was called with given invocation assertions.'); + $this->expectExceptionMessage('was called with given arguments.'); $this->assertThat($callable, new WasCalled(function (string $first, string $last): void { $this->assertSame('first', $first); @@ -140,7 +140,7 @@ public function itFailsWhenNotCalledExpectedTimesWithExpectedArguments(Quantable $quantise->applyTo(static fn() => $callable('first', 'last')); $this->expectException(ExpectationFailedException::class); - $this->expectExceptionMessage("was called {$quantise->expected} time(s) with given invocation assertions."); + $this->expectExceptionMessage("was called {$quantise->expected} time(s) with given arguments."); $this->assertThat( $callable, diff --git a/src/PHPUnit/DataProviders/QuantableConstraintTest.php b/src/PHPUnit/DataProviders/QuantableConstraintTest.php index d97d9a4..07b3437 100644 --- a/src/PHPUnit/DataProviders/QuantableConstraintTest.php +++ b/src/PHPUnit/DataProviders/QuantableConstraintTest.php @@ -64,7 +64,7 @@ public function itCanBeInvokedOnConstraints(string $method, int $times): void $instance($constraint); - $constraint->spy->assert(new WasCalled(new WithSameArguments($constraint))->once()); + $constraint->spy->assert(new WasCalled(new WithSameArguments($instance->method, $instance->times))->once()); } #[Test] From 7daf53a5f7ad94ab1764a7ecbfdccb5c66edb6c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Re=CC=81mi=20Pelhate?= Date: Fri, 5 Jun 2026 14:47:48 +0200 Subject: [PATCH 5/5] Improve handling of callable serialization in WasCalled failure output --- .../Callables/Assertions/WithEqualArguments.php | 15 +++++++++++++-- .../Assertions/WithEqualArgumentsTest.php | 9 ++++++--- .../Callables/Assertions/WithSameArguments.php | 15 +++++++++++++-- .../Assertions/WithSameArgumentsTest.php | 10 +++++++--- src/PHPUnit/Constraint/Callables/WasCalled.php | 17 +++++++++++++++++ 5 files changed, 56 insertions(+), 10 deletions(-) diff --git a/src/PHPUnit/Constraint/Callables/Assertions/WithEqualArguments.php b/src/PHPUnit/Constraint/Callables/Assertions/WithEqualArguments.php index 4e70ef6..778882e 100644 --- a/src/PHPUnit/Constraint/Callables/Assertions/WithEqualArguments.php +++ b/src/PHPUnit/Constraint/Callables/Assertions/WithEqualArguments.php @@ -20,10 +20,21 @@ public function __construct(mixed ...$expected) public function __invoke(mixed ...$actual): void { - Assert::assertCount(count($this->expected), $actual); + Assert::assertCount( + count($this->expected), + $actual, + 'Callable was invoked with a different amount of arguments.', + ); foreach ($actual as $key => $value) { - Assert::assertEquals($this->expected[$key], $value); + // @mago-expect analyzer:invalid-operand + $argumentNumber = $key + 1; + + Assert::assertEquals( + $this->expected[$key], + $value, + "Argument #{$argumentNumber} passed to callable does not match expected value.", + ); } } } diff --git a/src/PHPUnit/Constraint/Callables/Assertions/WithEqualArgumentsTest.php b/src/PHPUnit/Constraint/Callables/Assertions/WithEqualArgumentsTest.php index 3c92d1d..ffa5521 100644 --- a/src/PHPUnit/Constraint/Callables/Assertions/WithEqualArgumentsTest.php +++ b/src/PHPUnit/Constraint/Callables/Assertions/WithEqualArgumentsTest.php @@ -20,18 +20,20 @@ public function itFailsWhenInvokedWithDifferentAmountOfArguments(string ...$actu $instance = new WithEqualArguments('first', 'last'); $this->expectException(ExpectationFailedException::class); + $this->expectExceptionMessage('was invoked with a different amount of arguments'); $instance->__invoke(...$actual); } #[Test] - #[TestWith(['first different', 'last'])] - #[TestWith(['first', 'last different'])] - public function itFailsWhenInvokedWithDifferentArguments(string ...$actual): void + #[TestWith([1, 'first different', 'last'])] + #[TestWith([2, 'first', 'last different'])] + public function itFailsWhenInvokedWithDifferentArguments(int $argument, string ...$actual): void { $instance = new WithEqualArguments('first', 'last'); $this->expectException(ExpectationFailedException::class); + $this->expectExceptionMessage("Argument #{$argument} passed to callable does not match expected value"); $instance->__invoke(...$actual); } @@ -42,6 +44,7 @@ public function itFailsWhenInvokedWithArgumentsInDifferentOrder(): void $instance = new WithEqualArguments('first', 'last'); $this->expectException(ExpectationFailedException::class); + $this->expectExceptionMessage('Argument #1 passed to callable does not match expected value'); $instance->__invoke('last', 'first'); } diff --git a/src/PHPUnit/Constraint/Callables/Assertions/WithSameArguments.php b/src/PHPUnit/Constraint/Callables/Assertions/WithSameArguments.php index 0a5b5d8..3d9208b 100644 --- a/src/PHPUnit/Constraint/Callables/Assertions/WithSameArguments.php +++ b/src/PHPUnit/Constraint/Callables/Assertions/WithSameArguments.php @@ -20,10 +20,21 @@ public function __construct(mixed ...$expected) public function __invoke(mixed ...$actual): void { - Assert::assertCount(count($this->expected), $actual); + Assert::assertCount( + count($this->expected), + $actual, + 'Callable was invoked with a different amount of arguments.', + ); foreach ($actual as $key => $value) { - Assert::assertSame($this->expected[$key], $value); + // @mago-expect analyzer:invalid-operand + $argumentNumber = $key + 1; + + Assert::assertSame( + $this->expected[$key], + $value, + "Argument #{$argumentNumber} passed to callable does not match expected value.", + ); } } } diff --git a/src/PHPUnit/Constraint/Callables/Assertions/WithSameArgumentsTest.php b/src/PHPUnit/Constraint/Callables/Assertions/WithSameArgumentsTest.php index 2b18f84..ce7bb0a 100644 --- a/src/PHPUnit/Constraint/Callables/Assertions/WithSameArgumentsTest.php +++ b/src/PHPUnit/Constraint/Callables/Assertions/WithSameArgumentsTest.php @@ -20,18 +20,20 @@ public function itFailsWhenInvokedWithDifferentAmountOfArguments(string ...$actu $instance = new WithSameArguments('first', 'last'); $this->expectException(ExpectationFailedException::class); + $this->expectExceptionMessage('was invoked with a different amount of arguments'); $instance->__invoke(...$actual); } #[Test] - #[TestWith(['first different', 'last'])] - #[TestWith(['first', 'last different'])] - public function itFailsWhenInvokedWithDifferentArguments(string ...$actual): void + #[TestWith([1, 'first different', 'last'])] + #[TestWith([2, 'first', 'last different'])] + public function itFailsWhenInvokedWithDifferentArguments(int $argument, string ...$actual): void { $instance = new WithSameArguments('first', 'last'); $this->expectException(ExpectationFailedException::class); + $this->expectExceptionMessage("Argument #{$argument} passed to callable does not match expected value"); $instance->__invoke(...$actual); } @@ -42,6 +44,7 @@ public function itFailsWhenInvokedWithArgumentsInDifferentOrder(): void $instance = new WithSameArguments('first', 'last'); $this->expectException(ExpectationFailedException::class); + $this->expectExceptionMessage('Argument #1 passed to callable does not match expected value'); $instance->__invoke('last', 'first'); } @@ -52,6 +55,7 @@ public function itFailsWhenInvokedWithNonIdenticalArguments(): void $instance = new WithSameArguments(new stdClass()); $this->expectException(ExpectationFailedException::class); + $this->expectExceptionMessage('Argument #1 passed to callable does not match expected value'); $instance->__invoke(new stdClass()); } diff --git a/src/PHPUnit/Constraint/Callables/WasCalled.php b/src/PHPUnit/Constraint/Callables/WasCalled.php index 148cba9..3ddb02d 100644 --- a/src/PHPUnit/Constraint/Callables/WasCalled.php +++ b/src/PHPUnit/Constraint/Callables/WasCalled.php @@ -16,6 +16,7 @@ use function array_filter; use function count; use function is_callable; +use function spl_object_id; final class WasCalled extends Constraint implements Quantable { @@ -80,6 +81,22 @@ private function wasInvokedWithExpectedArguments(CallableInvocation $invocation) return true; } + protected function failureDescription(mixed $other): string + { + if (!$other instanceof SpyCallable) { + return parent::failureDescription($other); + } + + $message = $other::class . '#' . spl_object_id($other); + + if ($this->times !== null) { + $totalInvocations = count($other->invocations); + $message .= " (with {$totalInvocations} total invocations)"; + } + + return "{$message} {$this->toString()}"; + } + public function toString(): string { $message = 'was called';