在PHPUnit中,如何模拟父方法?

在PHPUnit中,如何模拟父方法?,php,testing,mocking,phpunit,Php,Testing,Mocking,Phpunit,我想测试一个调用同名父方法的类方法。有办法做到这一点吗 class Parent { function foo() { echo 'bar'; } } class Child { function foo() { $foo = parent::foo(); return $foo; } } class ChildTest extend PHPUnit_TestCase { funct

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

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

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


您可以模拟被测试对象的依赖关系。这意味着SUT需要做工作的任何其他对象。

以下是我如何做的,我不知道这是否正确,但它可以工作:

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);
}
怎么回事

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


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

一种适用于我的方法是在子类上实现对父类的wrap调用,最后模拟那些wrap

您修改了以下代码:

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

我完全同意@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”的父类,并将其视为存根,包括我的存根类(parent)和忽略到实际父类(设置为自动加载的实际父类,必须包括存根父类)

现在,我创建子类的模拟对象(通过模拟生成器),并使用assertSame的结尾完成测试用例。:-)


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

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

@james,在这种情况下,什么是单元?这是不可能的,因为您需要在
Parent
Child
之间插入一个模拟子类。我想我可以通过将对Parent方法的调用包装在其他可模拟方法中来修改子类。您仍然可以模拟sut自己的方法,例如,self-mock。@James,你的意思是什么?是的,但我不知道一种自分流父方法的方法。+1用于指出聚合。起初我认为继承更好,但在本例中,聚合更适合于测试目的和代码本身。具体地说,单元测试要求您模拟除测试方法之外的所有SUT使用的方法。通过在现有功能的基础上扩展对象并没有什么错,此时您需要合法地调用父方法。我也想知道这是否是最好的方法,因为我尝试介绍的遗留项目经常这样做。从技术上讲,您并不是在模仿父方法调用,所以这并不是对原始问题的准确答案。op需要做的是模拟whater()调用,而不是whater()本身所做的后续调用。@Oddman你部分正确,我认为Dallas的答案是正确的,因为它直截了当(即测试需要测试的任何东西)也就是说,在这个上下文中,varable被设置为某个值。这种方法的问题是,您在子级上实现方法纯粹是为了测试目的,这不是一种好的开发方法。我不理解您所说的“包括我的存根类(父级)而忽略实际的父级”是什么意思。你能告诉我你是怎么做的吗?这种方法很危险。假设您有一个类,Parent,您希望为其创建硬编码、手动编写的模拟,并忽略真正的父类(就像给定的示例)。PHP可能已经调用了另一个类autoloaded Parent,在这种情况下,当试图包含假类时,将出现致命的“无法重新声明”错误。但是,撇开危险不谈,这样做的方法只是编写一个小的php文件,里面定义了假类,然后手动从测试中包含该文件,这样就不必自动加载真正的类。@dalesikkema:在开始测试用例之前,我已经处理过了。我已经为测试用例编写了自己的自动加载器:P即使您可以参考Joomla github repo
$this->assertSame($mock, $mock->save());
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();
    }
}