2013-12-08 5 views
4

Я пишу несколько тестов для моего помощника просмотра. Это первый раз, когда я хотел бы что-то сделать с насмешливыми объектами. Я использую стандартную PHPUnit mocking Framework.PhpUnit mocking: function undefined

Я написал функцию, которая готовит мой фиктивный объект:

private function getTestStub(){ 
    $mockResult = array(); 
    $mock = $this->getMock('My\Entity\Product'); 
    $mock->expects($this->once()) 
       ->method('getId') 
       ->will($this->returnValue(1)); 
    $mock->expects($this->once()) 
     ->method('getName') 
     ->will($this->returnValue('jan')); 
    $mock->expects($this->once()) 
     ->method('getWoonplaats') 
     ->will($this->returnValue('Amsterdam')); 
    $mockResult[] = $mock; 
    return $mockResult; 
} 

Теперь при использовании этой заглушки для моих тестов я получаю следующую ошибку:

Fatal error: Call to undefined method Mock_Product_129abca6::getId() 

Что я делаю неправильно здесь?

+1

'Продукт :: getId' существует? Это не волшебный метод (например, что-то генерируемое '__call')? – chrisguitarguy

+0

Ну, это насмехается, как вы можете видеть в моем коде – sanders

+1

Вы должны опубликовать класс (или сопряженный), определенный в 'My \ Entity \ Product'. – chrisguitarguy

ответ

4

PHPUnit смотрит на класс, который вы пытаетесь высмеять через reflection или get_class_methods.

Классы, которые вы издеваетесь, если они существуют, распространяются макетными объектами. Аналогично реализуются интерфейсы. Вы можете указать see how this works в самом коде. Это куча генерации кода, и если вы хотите взглянуть и проследить, как она работает, то хорошей отправной точкой является PHPUnit_Framework_MockObject_Generator::generate.

Не видя ваш класс вы пытаетесь дразнить, я собираюсь предположить, что ваши добытчики являются «магической» метода порождены __call с чем-то вроде этого:

<?php 
namespace My\Entity; 

class Product 
{ 
    private $data = array(); 

    public function __call($method, $args) 
    { 
     $set_or_get = strtolower(substr($method, 0, 3)); 
     $prop = strtolower(substr($method, 3)); 
     if ('get' === $set_or_get) { 
      return isset($this->data[$prop]) ? $this->data[$prop] : null; 
     } elseif ('set' === $set_or_get && isset($args[0])) { 
      $this->data[$prop] = $args[0]; 
     } else { 
      throw new \BadMethodCallException(); 
     } 
    } 
} 

PHPUnit не могу на самом деле сделайте то, что хотите, потому что методы, которые вы пытаетесь вызвать, на самом деле не существуют, а магия __call не работает должным образом. Либо потому, что PHPUnit использует этот метод, то что-то другое (вам придется его искать, чтобы найти его). Чтобы обойти это, вам нужно tell PHPUnit which methods you want to include in your mock.

// the second argument let's you define methods 
$mock = $this->getMock('My\Entity\Product'); 
// try doing this instead 
$mock = $this->getMock('My\Entity\Product', array('getId', 'getName', 'getWoonplaats')); 

This question также имеет некоторые хорошие примеры выше, и обходные пути.

+0

Хмм, я не понял из руководства PHPUnit, что Mocking работает так. Итак, если метод mocket не существует в фактическом классе, то он не будет работать, и он скажет, что метод не определен. – sanders

+1

Спасибо за ответ, это помогло, когда я случайно высмеял «foo \ service \ foobar» вместо 'foo \ entity \ foobar'. Возможно, TLDR может улучшить этот ответ: 'method ('foo') -> willReturn ('bar')' ничего не сделает, если класс, издевавшийся, не определяет этот метод. – AlexMA