首页 > 解决方案 > PHPUnit - 调用断言特征方法

问题描述

假设我有一个当前有方法的特征:

trait MyTrait
{
    public function traitMethod()
    {
        return true;
    }
}

现在假设这个特性被几个类使用,但我不想为每个类编写一个单元测试。相反,我只想为 trait 编写一个单元测试:

public function testTraitMethod()
{
    $trait = $this->getMockForTrait(MyTrait::class);
    $this->assertTrue($trait->traitMethod());
}

但问题是一个类实际上可能会覆盖 trait 的方法:

class MyClass
{
    use MyTrait;

    public function traitMethod()
    {
        return false;
    }
}

在这种情况下MyClass是做错了什么,但我不知道,因为我只测试这个特征。

我的想法是为每个类编写一个单元测试,以检查它是否正在使用该特征并且它没有覆盖该方法。如果一个类需要覆盖 trait 的方法,那么它也需要一个特定的单元测试。

目前我正在为每个实现我的特征的类编写单元测试,但它当然是到处都是复制粘贴测试。

那么有没有办法测试一个类是否调用它的底层特征方法?

标签: phpunit-testingmockingphpunit

解决方案


我找到了一个解决方案Reflection,我会发布它以防有人需要它,因为我找不到与我的问题相关的任何内容。如果需要,请随意评论或添加不同的解决方案。

因此,以下测试断言$serviceClass使用$traitClass并且不覆盖$traitClass除抽象方法和手动添加到$overriddenMethods数组中的方法外声明的方法。

public function testServiceUsesTrait()
{
    $serviceClass = MyClass::class;
    $traitClass = MyTrait::class;

    $this->assertContains($traitClass, (new \ReflectionClass($serviceClass))->getTraitNames());

    $reflectedTrait = new \ReflectionClass($traitClass);
    $reflectedTraitFile = $reflectedTrait->getFileName();

    /**
     * If $serviceClass overrides some trait methods they
     * should be inserted into this array to avoid test failure.
     * Additional unit-tests should be written for overridden methods.
     */
    $overriddenMethods = [];

    foreach ($reflectedTrait->getMethods() as $traitMethod) {
        if ($traitMethod->isAbstract() || in_array($traitMethod->getName(), $overriddenMethods, true)) {
            continue;
        }
        $classMethod = new \ReflectionMethod($serviceClass, $traitMethod->getName());
        $this->assertSame($reflectedTraitFile, $classMethod->getFileName(), sprintf(
            'Trait method "%s" is overridden in class "%s" thus it must be excluded from this test.',
            $traitMethod->getName(), $serviceClass
        ));
    }
}

我还尝试使用$classMethod->getDeclaringClass()而不是比较文件名来比较声明类,但它没有用:即使类中没有重写 trait 方法,也getDeclaringClass()总是返回类本身。


推荐阅读