2013-04-19 4 views
7

Я новичок в phpunit и прочитал документацию по макету объектов, но это не очень понятно.PHPUnit mocks - метод assert под названием

Я пытаюсь написать простой тест, который утверждает метод внутри класса. С помощью следующего кода я проверяю, что при вызове Client :: exchangeArray выполняется вызов Client :: getInputFilter.

class Client implements InputFilterAwareInterface 
{ 

public function getInputFilter() { 
    if(!$this->_inputFilter){ 
     $inputFactory = new InputFactory(); 
     $inputFilter = new InputFilter(); 

     $inputFilter->add($inputFactory->createInput(array(
      'name' => 'id', 
      'required' => true, 
      'filters' => array(
       array(
        'name' => 'Int' 
       ) 
      ) 
     ))); 

     $inputFilter->add($inputFactory->createInput(array(
      'name' => 'name', 
      'required' => true, 
      'filters' => array(
       array(
        'name' => 'StripTags' 
       ), 
       array(
        'name' => 'StringTrim' 
       ), 
       array(
        'name' => 'StripNewLines'  
       ), 
       array(
        'name' => 'Alpha' 
       ) 
      ), 
      'validators' => array(
       array(
        'name' => 'StringLength', 
        'options' => array(
         'encoding' => 'UTF-8', 
         'min' => 2, 
         'max' => 100 
        ) 
       ) 
      ) 
     ))); 

     $inputFilter->add($inputFactory->createInput(array(
      'name' => 'surname', 
      'required' => true, 
      'filters' => array(
       array(
        'name' => 'StripTags' 
       ), 
       array(
        'name' => 'StringTrim' 
       ) 
      ), 
      'validators' => array(
       array(
        'name' => 'StringLength', 
        'options' => array(
         'encoding' => 'UTF-8', 
         'min' => 2, 
         'max' => 100 
        ) 
       ) 
      ) 
     ))); 

     $inputFilter->add($inputFactory->createInput(array(
      'name' => 'email', 
      'required' => false, 
      'filters' => array(
       array(
        'name' => 'StripTags' 
       ), 
       array(
        'name' => 'StringTrim' 
       ) 
      ), 
      'validators' => array(
       array(
        'name' => 'StringLength', 
        'options' => array(
         'encoding' => 'UTF-8', 
         'min' => 2, 
         'max' => 150 
        ) 
       ), 
       array(
        'name' => 'EmailAddress' 
       ) 
      ) 
     ))); 

     $this->_inputFilter = $inputFilter; 
    } 
    return $this->_inputFilter; 
} 

public function exchangeArray($data){ 
    $inputFilter = $this->getInputFilter(); 
    $inputFilter->setData($data); 
    if(!$inputFilter->isValid()){ 
     throw new \Exception('Invalid client data'); 
    } 

    $cleanValues = $inputFilter->getValues(); 

    $this->_id = (isset($cleanValues['id']) ? $cleanValues['id'] : null); 
    $this->_name = (isset($cleanValues['name']) ? $cleanValues['name'] : null); 
    $this->_surname = (isset($cleanValues['surname']) ? $cleanValues['surname'] : null); 
    $this->_email = (isset($cleanValues['email']) ? $cleanValues['email'] : null); 
    }   
} 

Вот мой тест:

public function testExchangeArrayCallsInputFilter(){ 
    $data = array('id' => 54, 
      'name' => 'john', 
      'surname' => 'doe', 
      'email' => '[email protected]' 
    ); 

    $mock = $this->getMock('Client', array('exchangeArray')); 
    $mock->expects($this->once()) 
     ->method('getInputFilter'); 
    $mock->exchangeArray($data); 
} 

... и я получаю следующее сообщение об ошибке:

Expectation failed for method name is equal to when invoked 1 time(s). Method was expected to be called 1 times, actually called 0 times.

Где я буду неправильно?

ответ

9

Все зависит от того, что вы хотите проверить и что вы хотите издеваться. Основываясь на названии вашего теста, я предполагаю, что вам нужен тест exchangeArray.

Метод getMock принимает в качестве второго аргумента имена методов, которые вы хотите имитировать. Это означает, что их никогда не называют.

Итак, если вы хотите тест exchangeArray метод и издеваться getInputFilter вы должны пройти «getInputFilter» во втором аргументе, как показано ниже:

$mock = $this->getMock('Client', array('getInputFilter')); 
$mock->expects($this->once()) 
    ->method('getInputFilter'); 
$mock->exchangeArray($data); 

Но будьте осторожны. Вы не сказали своему макету что-либо вернуть, поэтому он вернет нулевое значение. Это означает, что вы получите фатальную ошибку во второй строке метода exchangeArray (попытка вызова метода для не-объекта). Вы должны подготовить некоторые подделать объект фильтра, чтобы иметь дело с, например:

// $preparedFilterObject = ... 
$mock = $this->getMock('Client', array('getInputFilter')); 
$mock->expects($this->once()) 
    ->method('getInputFilter') 
    ->will($this->returnValue($preparedFilterObject); 
$mock->exchangeArray($data); 

И если вы хотите вызвать «реальный» getInputFilter метод - тогда вы просто не можете издеваться этот метод.

+0

Спасибо за ответ. У меня уже есть реализации для обоих методов. Я просто хотел проверить, что методы вызываются в правильном порядке, а затем они вызываются с определенными параметрами. Есть ли способ проверить, что методы вызываются без макетных объектов? –

+0

Нет, нет. Но если вы хотите вызвать второй метод, вам не нужно проверять, действительно ли он был вызван, потому что, если он не был, вы не получите объект $ inputFilter, и вы все равно потерпите неудачу. – Cyprian

+1

С другой стороны, если ваш тестовый проход и второй метод не были вызваны (не произойдет в вашем случае, но в некоторых случаях могут), вы заметите это в отчете о покрытии кода. И это может означать либо то, что вам просто не нужен этот второй звонок, либо вы исправите свои тесты. В случае, если ваш второй метод установил некоторое внутреннее свойство объекта, вы также можете проверить это свойство, чтобы проверить, был ли вызван метод. – Cyprian