测试覆盖特征方法的执行

时间:2018-08-04 08:45:50

标签: php unit-testing testing phpunit mockery

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

当我有这样的代码时:

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;
    }
}

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

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

<?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方法。有没有办法用这样的代码编写这样的测试?

1 个答案:

答案 0 :(得分:2)

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

检查下面的代码

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();
    }
}

如果您自动加载特征,可以尝试使用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.像使用它一样进行测试。

类用户必须只知道公共接口才能使用它,为什么测试应该有任何不同? 一个内部方法调用不同的事实是实现细节,并对此进行测试以破坏封装。如果某天您将从特质转换为类方法而不更改类行为,则即使外部类看起来相同,也将不得不修改测试。

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

  

大多数时候,您应该能够通过练习课堂来测试它   公共方法。如果有重要功能被隐藏   在私人或受保护的访问之后,可能是一个警告信号,   那里还有另一个班级努力奋斗。