【问题标题】:PHPUnit mock method in another class另一个类中的 PHPUnit 模拟方法
【发布时间】:2019-07-29 06:40:38
【问题描述】:

我开始使用 PHPUnit 来测试和模拟 php 中的方法。如果方法在同一个类中,则没有问题。
但我还不明白如何模拟另一个类中的方法。

class classA {
  public function funcA() {
    $classB = new classB();
    $value = $classB->getValue();
    return $value;
  }
}
class classB {
  public function getValue() {
    return "this is my value";
  }
}

如果 getValue 属于 A 类,我可以执行以下操作:

class testClassA {
  function testFuncA() {
    $mb = $this
      ->getMockBuilder(classA::class)
      ->setMethods(array('getValue'))
      ->getMock();
    $mb
      ->method('getValue')
      ->will($this->returnValue('my new value');
    $value = $mb->funcA();

    $this->assertEquals('my new value', $value);
  }
}

但是在我们上面的情况下,我该怎么做呢?

编辑:
Jon Stirling 要求在构造函数而不是测试方法中实例化 classB
所以让我们编辑classA

class classA {

  $private classB;

  public function __construct(){
    $this->classB = new classB();
  }

  public function funcA() {
    $value = $this->classB->getValue();
    return $value
  }
}

那怎么可能呢?

【问题讨论】:

  • 理想情况下,您会将 classB 的实例注入到 classA 中(通过构造函数、setter 或作为 funcA 的方法 arg),但是由于您在要测试的方法中实例化 classB,因此您已经很难模拟(我想你可以操纵自动加载器)。
  • 感谢您的评论。我现在编辑了帖子。你有什么想法,我现在应该怎么做?
  • 这根本不是我说的。我说它应该注入对象。你所做的只是移动你在同一个类中实例化它的位置。
  • 好吧,我误解了..但我认为在我的情况下这是不可能的..
  • 您的testFuncA 测试实际上并没有测试任何东西。它模拟出一个方法并测试模拟出的方法返回假数据。它实际上并没有运行您的任何代码。

标签: php mocking phpunit


【解决方案1】:

代替实际解耦您的类以便测试可以在测试时注入classB 对象,答案是使用反射来替换类成员:

class classA {

    private $classB;

    public function __construct(){
        $this->classB = new classB();
    }

    public function funcA() {
        $value = $this->classB->getValue();
        return $value
    }
}


class testClassA {
    function testFuncA() {
        $mb = $this
            ->getMockBuilder(classB::class)
            ->setMethods(array('getValue'))
            ->getMock();

        $mb
            // XXX must be called EXACTLY once
            ->expects($this->once())
            ->method('getValue')
            ->will($this->returnValue('my new value');

        $ma = $this
            ->getMockBuilder(classA::class)
            ->setMethods([
                // XXX do not mock any methods except the constructor!
            ])
            // XXX avoid any side effects from classA's constructor:
            ->disableOriginalConstructor()
            ->getMock();

        // XXX inject the mock B into the mock A
        $reflector = new ReflectionClass($ma);
        $prop = $reflector->getProperty('classB');
        $prop->setAccessible(true);
        $prop->setValue($ma, $mb);

        $value = $ma->funcA();

        $this->assertEquals('my new value', $value);
    }
}

->expects() 运算符算作一个断言。所有这些测试测试都是classA 在其$classB 成员上调用getValue 方法。不太有用,IMO。

【讨论】:

  • 感谢您的回答。我尝试以这种方式进行,但是当我尝试执行测试时,我得到了异常ReflectionException : Property classB does not exist 我知道,整个测试现在没有用,但是我在不同的场景中使用它有道理,我认为:D
猜你喜欢
  • 2014-05-05
  • 2022-01-04
  • 1970-01-01
  • 2013-04-16
  • 2013-06-04
  • 2014-04-03
  • 1970-01-01
  • 2021-06-23
  • 2013-09-12
相关资源
最近更新 更多