В PHPUnit, как мне макет родительских методов?

17

Я хочу протестировать метод класса, который вызывает родительский метод с тем же именем. Есть ли способ сделать это?

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 15.07.2011 в 20:33
источник
  • я передам это моей команде –  james 15.07.2011 в 20:42
  • @james, что такое единица в этом случае? –  takeshin 15.07.2011 в 20:59
  • Это невозможно, потому что вам нужно будет вставить поддельный подкласс между родителями и дочерними. –  David Harkness 15.07.2011 в 22:22
  • Я думаю, что могу просто изменить дочерний класс, обернув вызов родительскому методу каким-либо другим способом макетирования. –  james 15.07.2011 в 22:42

5 ответов

16

Вы не издеваетесь или не заглушаете методы в тесте-тестировании (SUT). Если вы чувствуете, что вам нужно высмеять или заглушить метод в родительском элементе SUT, скорее всего, вы не должны использовать наследование, а агрегацию.

Вы издеваетесь над зависимостями объекта-теста. Это означает, что любые другие объекты, которые SUT требует выполнения работы.

    
ответ дан Gordon 15.07.2011 в 21:02
источник
  • , вы все же можете высмеивать собственные методы сут, например. самооценка. –  james 15.07.2011 в 22:41
  • @ Джеймс, вы имеете в виду Self Shunting? –  Gordon 16.07.2011 в 09:55
  • да, но я не знаю о способе самостоятельного шунта родительского метода. –  james 17.07.2011 в 17:07
  • +1 для указания агрегации. Первоначально я думал, что наследование лучше, но в этом случае агрегация более подходит - как для целей тестирования, так и для самого кода. –  Szymon Sadło 14.07.2016 в 10:56
  • Ничего себе, это неправильно на стольких уровнях. В частности, модульное тестирование требует, чтобы вы издевались над всеми используемыми методами SUT, за исключением тестируемого метода. Нет ничего плохого в расширении объекта, основываясь на его существующей функциональности, и это тогда, когда вам будет законно нужно вызвать родительские методы. –  XedinUnknown 20.12.2017 в 16:09
8

Подход, который работает для меня, - это реализация обертывания родительского вызова дочернего класса и, наконец, имитация этих оберток.

Вы изменили код:

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?
    }
 }
    
ответ дан user1029767 24.10.2012 в 16:10
источник
  • проблема с этим подходом заключается в том, что вы реализуете методы для ребенка исключительно для целей тестирования, что не является хорошим подходом к разработке. –  Oddman 11.08.2013 в 19:33
  • И тогда вы не можете тестировать unit parentFooCall () –  Edson Medina 17.10.2014 в 16:46
4

Вот как я это сделал, я понятия не имею, правильно ли это, но он работает:

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

что?

Мое рассуждение здесь состоит в том, что все, что я действительно хочу проверить, это то, была ли установлена ​​моя переменная. я действительно не забочусь о том, что происходит в родительском методе, но так как вы не можете запретить вызов родительского метода, что я делаю, это mock зависимые методы родительского метода.

теперь иди и скажи мне, что я не прав:)

    
ответ дан Dallas Caley 21.03.2012 в 21:21
источник
  • Я бы тоже хотел знать, если это лучший подход, поскольку старый проект, который я пытаюсь осветить, делает это много. –  qrazi 08.02.2013 в 13:55
  • технически вы не издеваетесь над вызовом родительского метода, так что это не совсем точный ответ на исходный вопрос. То, что нужно сделать op, издевается над вызовом any (), а не с последующим вызовом, который делает что-либо (). –  Oddman 11.08.2013 в 19:34
  • @Oddman, вы частично правы, я думаю, что ответ Далласа верен, потому что он прямо к точке (которая должна проверять все, что нужно протестировать), т. е. что в этом контексте переменная устанавливается на что-то –  Ahmad Hajjar 23.05.2017 в 08:08
2

Я полностью согласен с @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();
    }
}

Я создал еще один родительский класс с тем же именем «Родитель» и рассматриваю как заглушку и включаю мой класс-заглушку (родительский) и игнорирую фактического родителя (должен быть включен активный набор родительских классов в автонагрузку и родительский заглушку )

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

Теперь я создаю макет объекта класса Child (через Mock-builder) и завершу свои тестовые примеры с завершением assertSame. : -)

$this->assertSame($mock, $mock->save());
    
ответ дан Manish Trivedi 14.09.2013 в 11:43
источник
  • Я не понимаю, что вы подразумеваете под «включить мой класс заглушки (родительский) и игнорировать фактического родителя». Не могли бы вы показать, как вы это делаете? –  Daryl Spitzer 03.04.2014 в 17:44
  • Этот подход может быть опасным. Скажем, у вас есть класс, Parent, который вы хотите создать жестко закодированным, вручную написанным макетом и игнорировать настоящий класс родителя (как и в данном примере). PHP мог бы уже, из-за вызова в другом классе, автоматически загружать родителя, и в этом случае будет возникать фатальная ошибка «не может переопределить» при попытке включить поддельный класс. Но, в отличие от этого, способ сделать это - просто написать небольшой файл php с фальшивым классом, определенным внутри, и вручную включить этот файл из теста, поэтому реальный класс не должен быть автоматически загружен. –  russell 06.05.2015 в 22:05
  • @dalesikkema: Я уже обработал его перед началом тестовых случаев. Я пишу свой автозагрузчик для тестовых случаев. : P Даже вы можете взять ссылку с Joomla github repo –  Manish Trivedi 07.05.2015 в 14:18
0

На мой взгляд, удовлетворительным решением является создание класса, который наследуется от вашего тестируемого класса и переопределяет реализацию метода, который вы хотите предоставить другой реализации. У этого есть свои недостатки: он не всегда работает, например. для уже переопределенных методов и для частных методов.

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();
    }
} 
    
ответ дан physicalattraction 21.09.2015 в 15:20
источник