【问题标题】:Testing overriden trait method execution测试重写的特征方法执行
【发布时间】:2019-01-12 00:23:24
【问题描述】:

我有这样的情况。我有一些第 3 方特征(我不想测试)并且我有使用此特征的特征,并且在某些情况下运行第 3 方特征方法(在下面的示例中,我总是运行它)。

当我有这样的代码时:

use Mockery;
use PHPUnit\Framework\TestCase;

class SampleTest extends TestCase
{
    /** @test */
    public function it_runs_parent_method_alternative()
    {
        $class  = Mockery::mock(B::class)->makePartial();

        $class->shouldReceive('fooX')->once();

        $this->assertSame('bar', $class->foo());
    }

    protected function tearDown()
    {
        Mockery::close();
    }
}

trait X {
    function foo() {
        $this->something->complex3rdpartyStuff();
    }
}

trait Y2 {

    function foo() {
        $this->fooX();
        return 'bar';
    }
}

class B {
    use Y2, X {
        Y2::foo insteadof X;
        X::foo as fooX;
    }
}

它会正常工作,但我不希望代码像这样组织。在上面的类代码中,我使用了这两个特征,但在代码中我想测试实际上 trait 使用了开头提到的其他特征。

但是当我有这样的代码时:

<?php

use Mockery;
use PHPUnit\Framework\TestCase;

class SampleTest extends TestCase
{
    /** @test */
    public function it_runs_parent_method()
    {
        $class  = Mockery::mock(A::class)->makePartial();

        $class->shouldReceive('fooX')->once();

        $this->assertSame('bar', $class->foo());
    }

    protected function tearDown()
    {
        Mockery::close();
    }
}

trait X {
    function foo() {
        $this->something->complex3rdpartyStuff();
    }
}

trait Y {
    use X {
        foo as fooX;
    }

    function foo() {
        $this->fooX();
        return 'bar';
    }
}

class A {
    use Y;
}

我明白了:

未定义的属性 $something

所以在这种情况下,Mockery 似乎不再模拟 X::foo 方法了。有没有办法用这样组织的代码编写这样的测试?

【问题讨论】:

    标签: php unit-testing testing phpunit mockery


    【解决方案1】:

    到目前为止,还无法模拟更深层次的别名方法。您可以使用本地方法代理别名方法调用并允许模拟受保护的方法。

    检查下面的代码

    use Mockery;
    use PHPUnit\Framework\TestCase;
    
    class SampleTest extends TestCase
    {
        /** @test */
        public function it_runs_parent_method()
        {
            $mock  = Mockery::mock(A::class)->shouldAllowMockingProtectedMethods()->makePartial();
    
            $mock->shouldReceive('proxyTraitCall')->once();
    
            $this->assertSame('bar', $mock->foo());
        }
    
        protected function tearDown()
        {
            Mockery::close();
        }
    }
    
    trait X {
        function foo() {
            $this->something->complex3rdpartyStuff();
        }
    }
    
    trait Y {
        use X {
            foo as fooX;
        }
    
        function foo() {
            $this->proxyTraitCall();
            return 'bar';
        }
    
        function proxyTraitCall() {
            return $this->fooX();
        }
    }
    

    如果你自动加载 trait,你可以尝试使用 Mockery overload 它。

    /** @test */
    public function it_runs_parent_method()
    {
        $trait = Mockery::mock("overload:" . X::class);
        $trait->shouldReceive('foo')->once();
    
        $class  = Mockery::mock(A::class)->makePartial();
    
        $this->assertSame('bar', $class->foo());
    }
    

    Don't test implementation details. 像使用它一样测试它。

    类用户只需要知道公共接口才能使用它,为什么测试应该有什么不同? 事实上,一个内部方法调用不同的一个是实现细节,并且测试这会破坏封装。如果有一天你会在不改变类行为的情况下从 trait 切换到类方法,你将不得不修改测试,即使外部的类看起来是一样的。

    来自 Dave Thomas 和 Andy Hunt 的 实用单元测试

    大多数时候,你应该能够通过练习来测试一个类 公共方法。如果有隐藏的重要功能 在私有或受保护访问之后,这可能是一个警告信号 那里有另一个班级正在努力离开。

    【讨论】:

    • 不是这样,我需要用 trait 覆盖 3rd 方方法,我没有其他方法可以做到这一点。我想对它进行测试,因为它是作为 Composer 包提供的。
    • @MarcinNabiałek 我不确定我是否做对了。您具有提供某些功能并且想要模拟的特征?你能把这个特性包装在类中并将其作为依赖项提供吗?然后您可以提供替代实现
    • 不幸的是我不能。我需要使用覆盖其他特征方法的特征,并在某些情况下启动此方法。这就是为什么我希望有可能模拟原始特征方法,因为这是第 3 方,我只想测试它是否已启动。
    • @MarcinNabiałek 我想我不明白你的问题。它是开源的,你能提供原始用例的链接吗?
    • github.com/mnabialek/laravel-quick-migrations/blob/master/src/… 这是我开发的包中的文件。原始特征:github.com/laravel/framework/blob/5.6/src/Illuminate/Foundation/… 用例 - 在测试类中将 use DatabaseMigrations; 替换为 use QuickDatabaseMigrations;,无需进行任何其他更改以更快地运行它们
    猜你喜欢
    • 2020-04-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多