【问题标题】:In PHPUnit, how do I mock parent methods?在 PHPUnit 中,我如何模拟父方法?
【发布时间】:2011-10-06 09:36:48
【问题描述】:

我想测试一个调用同名父方法的类方法。有没有办法做到这一点?

class Parent {

    function foo() {
        echo 'bar';
    }
}

class Child {

    function foo() {
            $foo = parent::foo();
            return $foo;
    }
}

class ChildTest extend PHPUnit_TestCase {

    function testFoo() {
        $mock = $this->getMock('Child', array('foo'));

        //how do i mock parent methods and simulate responses?
    }
}

【问题讨论】:

  • @james,在这种情况下,unit 是什么?
  • 这是不可能的,因为您需要在 ParentChild 之间插入一个模拟子类。
  • 我想我可以通过将调用父方法包装在其他一些可模拟的方法中来修改子类。

标签: php testing mocking phpunit


【解决方案1】:

您不会在被测对象 (SUT) 中模拟或存根方法。如果您觉得需要在 SUT 的父级中模拟或存根方法,这可能意味着您不应该使用继承,而是使用聚合。

你模拟了被测对象的依赖关系。这意味着 SUT 需要执行工作的任何其他对象。

【讨论】:

  • 好吧,您仍然可以模拟 sut 自己的方法,例如自我模拟。
  • @James 你的意思是Self Shunting
  • 是的,但我不知道有一种方法可以自行分流父方法。
  • +1 用于指出聚合。最初我认为继承更好,但在这种情况下聚合更适合 - 用于测试目的和代码本身。
  • 哇,这在很多层面上都是错误的。具体来说,unit 测试要求您模拟除已测试方法之外的所有 SUT 使用的方法。在对象现有功能的基础上扩展对象并没有错,而这正是您合理需要调用父方法的时候。
【解决方案2】:

这是我的做法,我不知道这是否正确,但它有效:

class parentClass {
    public function whatever() {
        $this->doSomething();
    }
}

class childClass extends parentClass {
    public $variable;
    public function subjectUnderTest() {
        $this->variable = 'whocares';
        parent::whatever();
    }
}

现在我在做测试:

public function testSubjectUnderTest() {
    $ChildClass = $this->getMock('childClass', array('doSomething'))
    $ChildClass->expects($this->once())
               ->method('doSomething');
    $ChildClass->subjectUnderTest();
    $this->assertEquals('whocares', $ChildClass->variable);
}

什么?

我的理由是,我真正想要测试的是我的变量是否已设置。我并不真正关心父方法中发生的事情,但是由于您无法阻止调用父方法,所以我所做的就是模拟父方法的依赖方法。

现在继续告诉我我错了:)

【讨论】:

  • 我也想知道这是否是最好的方法,因为我试图涵盖的遗留项目做了很多......
  • 从技术上讲,您并不是在模拟父方法调用,因此它并不是对原始问题的准确答案。 op 需要做的是模拟whatever() 调用,而不是whatever() 本身进行的后续调用。
  • @Oddman 你部分正确,我认为达拉斯的回答是正确的,因为它直截了当(即测试任何需要测试的内容),即 varable 被设置为此上下文
【解决方案3】:

对我有用的一种方法是对子类的父调用执行包装,最后模拟这些包装。

你修改了代码:

class Parent {

    function foo() {
        echo 'bar';
    }
}

class Child {

    function foo() {
            $foo = $this->parentFooCall();
            return $foo;
    }
    function parentFooCall() {
            return parent::foo();
    }
}

class ChildTest extend PHPUnit_TestCase {

    function testFoo() {
        $mock = $this->getMock('Child', array('foo', 'parentFooCall'));

        //how do i mock parent methods and simulate responses?
    }
 }

【讨论】:

  • 这种方法的问题是你在孩子身上实现方法纯粹是为了测试目的,这不是一个好的开发方法。
【解决方案4】:

我完全同意@Gordon。我有同样的问题,但我尝试了一些棘手的概念。

我的场景是这样的

class Parent { // Actual-Parent Class
    function save() {
        // do something
        return $this
    }
}

class Child extends Parent {
   // Subject under test
    function save() {
          // do something
          return parent::save();
    }
}

我创建了另一个具有相同名称“Parent”的父类并将其视为存根并包含我的存根类(父级)并忽略实际父类(设置为自动加载和存根父级的实际父类必须包含在内)

class Parent { //Stub-Parent class
    function save() {
        return $this
    }
}

现在我创建 Child 类的模拟对象(通过 Mock-builder),并以 assertSame 结尾完成我的测试用例。 :-)

$this->assertSame($mock, $mock->save());

【讨论】:

  • 我不明白您所说的“包含我的存根类(父级)并忽略实际父级”是什么意思。你能展示一下你是怎么做到的吗?
  • 这种方法可能很危险。假设您有一个类 Parent,您想为其创建一个硬编码、手动编写的模拟,并忽略真正的 Parent 类(就像给定的示例一样)。由于在另一个类中的调用,PHP 可能已经自动加载了 Parent,在这种情况下,当尝试包含假类时将出现致命的“无法重新声明”错误。但是,除了危险之外,这样做的方法就是编写一个包含内部定义的假类的小 php 文件,并从测试中手动包含该文件,因此不必自动加载真正的类。
  • @dalesikkema :在开始测试用例之前我已经处理过了。我已经为测试用例编写了自己的自动加载器。 :P 即使你可以参考 Joomla github repo
【解决方案5】:

在我看来,一个令人满意的解决方案是创建一个从您的被测类继承的类,并覆盖您想要提供另一个实现的方法的实现。这有其缺陷:它并不总是有效,例如对于已经被覆盖的方法和私有方法。

class Parent
{
    function bar()
    {
        echo 'bar';
    }
}

class Child extends Parent
{
    function foo()
    {
        parent::bar();
        echo 'foo';
    }
}

class mockChild extends Child
{
    function bar()
    {
        echo 'baz';
    }
}

class ChildTest extends PHPUnit_TestCase 
{
    function testFoo() {
        $sut = new mockChild();
        $sut->foo();
    }
} 

【讨论】:

    猜你喜欢
    • 2013-01-27
    • 2019-08-07
    • 2013-05-30
    • 2013-02-02
    • 2021-06-16
    • 2019-11-27
    • 2016-06-13
    • 2021-04-27
    • 1970-01-01
    相关资源
    最近更新 更多