diff --git a/src/Exceptions/NoDirtyTestsFound.php b/src/Exceptions/NoDirtyTestsFound.php index 8c5ef1cb..58f2e7b5 100644 --- a/src/Exceptions/NoDirtyTestsFound.php +++ b/src/Exceptions/NoDirtyTestsFound.php @@ -14,7 +14,7 @@ use Symfony\Component\Console\Output\OutputInterface; /** * @internal */ -final class NoDirtyTestsFound extends InvalidArgumentException implements ExceptionInterface, RenderlessEditor, RenderlessTrace, Panicable +final class NoDirtyTestsFound extends InvalidArgumentException implements ExceptionInterface, Panicable, RenderlessEditor, RenderlessTrace { /** * Renders the panic on the given output. diff --git a/src/Expectation.php b/src/Expectation.php index aaa9b83e..0109d41e 100644 --- a/src/Expectation.php +++ b/src/Expectation.php @@ -501,6 +501,19 @@ final class Expectation ); } + /** + * Asserts that the given expectation target have a specific method. + */ + public function toHaveMethod(string $method): ArchExpectation + { + return Targeted::make( + $this, + fn (ObjectDescription $object): bool => $object->reflectionClass->hasMethod($method), + 'to have method', + FileLineFinder::where(fn (string $line): bool => str_contains($line, 'class')), + ); + } + /** * Asserts that the given expectation target is enum. */ diff --git a/src/Expectations/OppositeExpectation.php b/src/Expectations/OppositeExpectation.php index e857009d..2df6e91f 100644 --- a/src/Expectations/OppositeExpectation.php +++ b/src/Expectations/OppositeExpectation.php @@ -149,6 +149,19 @@ final class OppositeExpectation ); } + /** + * Asserts that the given expectation target does not have a specific method. + */ + public function toHaveMethod(string $method): ArchExpectation + { + return Targeted::make( + $this->original, + fn (ObjectDescription $object): bool => ! $object->reflectionClass->hasMethod($method), + 'to not have method', + FileLineFinder::where(fn (string $line): bool => str_contains($line, 'class')), + ); + } + /** * Asserts that the given expectation target is not enum. */ diff --git a/tests/Features/Expect/toHaveMethod.php b/tests/Features/Expect/toHaveMethod.php index 421c1ddd..ffe2f743 100644 --- a/tests/Features/Expect/toHaveMethod.php +++ b/tests/Features/Expect/toHaveMethod.php @@ -1,28 +1,29 @@ expect('Tests\Fixtures\Arch\ToHaveMethod\HasMethod\HasMethod') + ->toHaveMethod('foo'); -test('pass', function () use ($object) { - expect($object)->toHaveMethod('foo') - ->and($object)->toHaveMethod('foo') - ->and($object)->not->toHaveMethod('fooNull'); -}); +test('opposite class has method') + ->throws(ArchExpectationFailedException::class) + ->expect('Tests\Fixtures\Arch\ToHaveMethod\HasMethod\HasMethod') + ->not->toHaveMethod('foo'); -test('failures', function () use ($object) { - expect($object)->toHaveMethod('bar'); -})->throws(ExpectationFailedException::class); +test('class has method via a parent class') + ->expect('Tests\Fixtures\Arch\ToHaveMethod\HasMethod\HasMethodViaParent') + ->toHaveMethod('foo'); -test('failures with message', function () use ($object) { - expect($object)->toHaveMethod(name: 'bar', message: 'oh no!'); -})->throws(ExpectationFailedException::class, 'oh no!'); +test('class has method via a trait') + ->expect('Tests\Fixtures\Arch\ToHaveMethod\HasMethod\HasMethodViaTrait') + ->toHaveMethod('foo'); -test('not failures', function () use ($object) { - expect($object)->not->toHaveMethod('foo'); -})->throws(ExpectationFailedException::class); +test('failure when the class has no method') + ->throws(ArchExpectationFailedException::class) + ->expect('Tests\Fixtures\Arch\ToHaveMethod\HasNoMethod\HasNoMethodClass') + ->toHaveMethod('foo'); + +test('class has no method') + ->expect('Tests\Fixtures\Arch\ToHaveMethod\HasNoMethod\HasNoMethodClass') + ->not->toHaveMethod('foo'); diff --git a/tests/Fixtures/Arch/ToHaveMethod/HasMethod/HasMethod.php b/tests/Fixtures/Arch/ToHaveMethod/HasMethod/HasMethod.php new file mode 100644 index 00000000..63c79b00 --- /dev/null +++ b/tests/Fixtures/Arch/ToHaveMethod/HasMethod/HasMethod.php @@ -0,0 +1,13 @@ +