diff --git a/src/Expectation.php b/src/Expectation.php index 32e22d3c..0df76ac2 100644 --- a/src/Expectation.php +++ b/src/Expectation.php @@ -693,4 +693,17 @@ final class Expectation { return ToBeUsedInNothing::make($this); } + + /** + * Asserts that the given expectation dependency is an invokable class. + */ + public function toBeInvokable(): ArchExpectation + { + return Targeted::make( + $this, + fn (ObjectDescription $object): bool => $object->reflectionClass->hasMethod('__invoke'), + 'to be invokable', + FileLineFinder::where(fn (string $line): bool => str_contains($line, 'class')) + ); + } } diff --git a/src/Expectations/OppositeExpectation.php b/src/Expectations/OppositeExpectation.php index 367dfb92..55261be8 100644 --- a/src/Expectations/OppositeExpectation.php +++ b/src/Expectations/OppositeExpectation.php @@ -365,6 +365,19 @@ final class OppositeExpectation throw InvalidExpectation::fromMethods(['not', 'toBeUsedInNothing']); } + /** + * Asserts that the given expectation dependency is not an invokable class. + */ + public function toBeInvokable(): ArchExpectation + { + return Targeted::make( + $this->original, + fn (ObjectDescription $object): bool => ! $object->reflectionClass->hasMethod('__invoke'), + 'to not be invokable', + FileLineFinder::where(fn (string $line): bool => str_contains($line, 'class')) + ); + } + /** * Handle dynamic method calls into the original expectation. * diff --git a/tests/Features/Expect/toBeInvokable.php b/tests/Features/Expect/toBeInvokable.php new file mode 100644 index 00000000..c36567f7 --- /dev/null +++ b/tests/Features/Expect/toBeInvokable.php @@ -0,0 +1,29 @@ +expect('Tests\\Fixtures\\Arch\\ToBeInvokable\\IsInvokable\\InvokableClass') + ->toBeInvokable(); + +test('opposite class is invokable') + ->throws(ArchExpectationFailedException::class) + ->expect('Tests\\Fixtures\\Arch\\ToBeInvokable\\IsInvokable\\InvokableClass') + ->not->toBeInvokable(); + +test('class is invokable via a parent class') + ->expect('Tests\\Fixtures\\Arch\\ToBeInvokable\\IsInvokable\\InvokableClassViaParent') + ->toBeInvokable(); + +test('class is invokable via a trait') + ->expect('Tests\\Fixtures\\Arch\\ToBeInvokable\\IsInvokable\\InvokableClassViaTrait') + ->toBeInvokable(); + +test('failure when the class is not invokable') + ->throws(ArchExpectationFailedException::class) + ->expect('Tests\\Fixtures\\Arch\\ToBeInvokable\\IsNotInvokable\\IsNotInvokableClass') + ->toBeInvokable(); + +test('class is not invokable') + ->expect('Tests\\Fixtures\\Arch\\ToBeInvokable\\IsNotInvokable\\IsNotInvokableClass') + ->not->toBeInvokable(); diff --git a/tests/Fixtures/Arch/ToBeInvokable/IsInvokable/InvokableClass.php b/tests/Fixtures/Arch/ToBeInvokable/IsInvokable/InvokableClass.php new file mode 100644 index 00000000..24cbb979 --- /dev/null +++ b/tests/Fixtures/Arch/ToBeInvokable/IsInvokable/InvokableClass.php @@ -0,0 +1,13 @@ +