From dc9a1e8acef05e2bb5325faeab8d881a1f5b7d36 Mon Sep 17 00:00:00 2001 From: Malico Date: Sat, 20 Sep 2025 19:06:23 +0100 Subject: [PATCH] BugFix: Fix toUseTrait to detect inherited and nested traits --- src/Expectation.php | 17 ++++++++++++++++- tests/Features/Expect/toUseTrait.php | 16 ++++++++++++++++ .../ChildClassExtendingParent.php | 11 +++++++++++ .../ToUseTrait/HasNestedTrait/NestedTrait.php | 13 +++++++++++++ .../HasTrait/ParentClassWithTrait.php | 10 ++++++++++ .../HasTrait/TestTraitForInheritance.php | 17 +++++++++++++++++ 6 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 tests/Fixtures/Arch/ToUseTrait/HasInheritedTrait/ChildClassExtendingParent.php create mode 100644 tests/Fixtures/Arch/ToUseTrait/HasNestedTrait/NestedTrait.php create mode 100644 tests/Fixtures/Arch/ToUseTrait/HasTrait/ParentClassWithTrait.php create mode 100644 tests/Fixtures/Arch/ToUseTrait/HasTrait/TestTraitForInheritance.php diff --git a/src/Expectation.php b/src/Expectation.php index 5c8a076b7..f03a6c4b9 100644 --- a/src/Expectation.php +++ b/src/Expectation.php @@ -781,7 +781,22 @@ function (ObjectDescription $object) use ($traits): bool { return false; } - if (! in_array($trait, $object->reflectionClass->getTraitNames(), true)) { + $currentClass = $object->reflectionClass; + $usedTraits = []; + + do { + $classTraits = $currentClass->getTraits(); + foreach ($classTraits as $traitReflection) { + $usedTraits[$traitReflection->getName()] = $traitReflection->getName(); + + $nestedTraits = $traitReflection->getTraits(); + foreach ($nestedTraits as $nestedTrait) { + $usedTraits[$nestedTrait->getName()] = $nestedTrait->getName(); + } + } + } while ($currentClass = $currentClass->getParentClass()); + + if (! array_key_exists($trait, $usedTraits)) { return false; } } diff --git a/tests/Features/Expect/toUseTrait.php b/tests/Features/Expect/toUseTrait.php index fd9c933fd..806490b34 100644 --- a/tests/Features/Expect/toUseTrait.php +++ b/tests/Features/Expect/toUseTrait.php @@ -14,3 +14,19 @@ test('not failures', function () { expect('Pest\Expectations\HigherOrderExpectation')->not->toUseTrait('Pest\Concerns\Retrievable'); })->throws(ArchExpectationFailedException::class); + +test('trait inheritance - direct usage', function () { + expect('Tests\Fixtures\Arch\ToUseTrait\HasTrait\ParentClassWithTrait')->toUseTrait('Tests\Fixtures\Arch\ToUseTrait\HasTrait\TestTraitForInheritance'); +}); + +test('trait inheritance - inherited usage', function () { + expect('Tests\Fixtures\Arch\ToUseTrait\HasInheritedTrait\ChildClassExtendingParent')->toUseTrait('Tests\Fixtures\Arch\ToUseTrait\HasTrait\TestTraitForInheritance'); +}); + +test('trait inheritance - negative case', function () { + expect('Tests\Fixtures\Arch\ToUseTrait\HasInheritedTrait\ChildClassExtendingParent')->not->toUseTrait('NonExistentTrait'); +}); + +test('nested trait inheritance', function () { + expect('Tests\Fixtures\Arch\ToUseTrait\HasInheritedTrait\ChildClassExtendingParent')->toUseTrait('Tests\Fixtures\Arch\ToUseTrait\HasNestedTrait\NestedTrait'); +}); diff --git a/tests/Fixtures/Arch/ToUseTrait/HasInheritedTrait/ChildClassExtendingParent.php b/tests/Fixtures/Arch/ToUseTrait/HasInheritedTrait/ChildClassExtendingParent.php new file mode 100644 index 000000000..df7ce9ec3 --- /dev/null +++ b/tests/Fixtures/Arch/ToUseTrait/HasInheritedTrait/ChildClassExtendingParent.php @@ -0,0 +1,11 @@ +