【问题标题】:PHPUnit - Assert trait method is calledPHPUnit - 调用断言特征方法
【发布时间】:2019-11-15 08:29:55
【问题描述】:

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

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。

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

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

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

【问题讨论】:

  • 如果您担心 trait 方法被覆盖并返回不同的值,您是否可以测试 MyClass 并简单地断言来自 traitMethod() 的值是 trait 应该返回的,而不是是否调用了实际的方法?
  • @benJ 我在这里描述的只是一个例子。实际上,我有更复杂的东西,我检查是否在某些模拟上调用了某些方法以及是否正确处理了异常。

标签: php unit-testing mocking phpunit


【解决方案1】:

我找到了使用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() 总是返回类本身。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-10-04
    • 2013-09-15
    • 2013-10-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-16
    • 2016-09-29
    • 2016-12-02
    相关资源
    最近更新 更多