【问题标题】:Is it possible to override method of the class with plain PHP or Mockery?是否可以用普通的 PHP 或 Mockery 覆盖类的方法?
【发布时间】:2015-03-10 13:50:07
【问题描述】:

下面的例子可以很好地说明我的问题:

class a
{
    function a()
    {
         return file_get_contents('http://some/third/party/service');
    }
}

class b
{
    function b()
    {
        $a = new a();
        return $a->a() . ' Bar';
    }
}

class testB extends test
{
    function testB()
    {
        $b = new b();

        // Here we need to override a::a() method in some way to always return 'Foo'
        // so it doesn't depend on the third party service. We only need to check
        // the $b::b() method's behavior (that it appends ' Bar' to the string).
        // How do we do that?

        $this->assert_equals('Foo Bar', $b->b());
    }
}

让我指出,我无法控制“a”类的定义/包含位置。

【问题讨论】:

  • 查看反射 api。
  • @MarcelBurkhard 似乎没有提供必要的功能。
  • 这是 DI 或工厂可以帮助您在测试中消除此问题的地方:function b(a $a { return $a->a() . ' Bar'; } 使得模拟 a 的实例变得更加容易

标签: php testing mockery


【解决方案1】:

如果您更改了类 b 以便可以传入 a 的实例:

class b
{
    function b($a = null)
    {
        if ($a == null) {
            $a = new a();
        }

        return $a->a() . ' Bar';
    }
}

...那么为了测试,你可以使用像 Mockery 这样的框架来传入一个模拟的 'a' 实例,它总是返回 'Foo'

use \Mockery as m;

class testB extends test
{

    public function tearDown()
    {
        m::close();
    }

    public function testB()
    {
        $mockA = m::mock('a');
        $mockA->shouldReceive('a')->times(1)->andReturn('foo');

        $b = new b($mockA);

        $this->assert_equals('Foo Bar', $b->b());
    }

}

在此处查看 Mockery 的完整文档和示例:http://docs.mockery.io/en/latest/getting_started/simple_example.html

【讨论】:

    【解决方案2】:

    你可以像这样消除你的依赖:

    首先你创建一个接口,列出你需要的所有方法:

    interface Doer {
        function a();
    }
    

    然后为你创建一个适配器类:

    class ADoer implements Doer
    {
       protected $dependencyA;
    
       public function __construct(A $dep) {
           $this->dependencyA = $dep;
       }
    
       public function a() {
           $this->dependencyA->a();
       }
    }
    

    现在让你的 B 类依赖于 Doer 接口,而不是 A 实现:

    class B {
       private $doer;
       public function __construct(Doer $a) {
          $this->doer = $a;
       }
    
       public function b() {
          $this->doer->a();
       }
    
       public function setDoer(Doer $a) {
           $this->doer = $a;
       }
    
       //getDoer()
    }
    

    现在可以随意切换了:

    class FooDoer implements Doer {
       function a() {
          //do whatever you want
       }
    
    }
    $b->setDoer(new FooDoer());
    $b->b();
    

    【讨论】:

    • 谢谢!不幸的是,a's 和 b's 的代码已经存在,而且有太多需要重构(如果值得的话)。我想知道是否有一种简单的方法可以解决这个问题。
    • 所以...给我们展示一些真实的代码。也许有可能让它发挥作用。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-24
    • 2014-01-10
    相关资源
    最近更新 更多