2009-11-05 4 views
6

Я пытаюсь проверить, вызван ли защищенный метод в открытом интерфейсе.Тестирование, если был вызван защищенный метод

<?php 
abstract class SomeClassAbstract 
{ 
    abstract public foo(); 

    public function doStuff() 
    {  
     $this->_protectedMethod(); 
    } 

    protected function _protectedMethod(); 
    { 
     // implementation is irrelevant 
    } 
} 

<?php 
class MyTest extends PHPUnit_Framework_TestCase 
{ 
    public function testCalled() 
    { 
     $mock = $this->getMockForAbstractClass('SomeClass'); 
     $mock->expects($this->once()) 
      ->method('_protectedMethod'); 

     $mock->doStuff(); 
    } 
} 

Я знаю, что это называется правильно, но PHPUnit говорит, что его никогда не называли.

То же самое происходит, когда я проверяю другой путь, когда метод никогда называется:

<?php 
abstract class AnotherClassAbstract 
{ 
    abstract public foo(); 

    public function doAnotherStuff() 
    {  
     $this->_loadCache(); 
    } 

    protected function _loadCache(); 
    { 
     // implementation is irrelevant 
    } 
} 

<?php 
class MyTest extends PHPUnit_Framework_TestCase 
{ 
    public function testCalled() 
    { 
     $mock = $this->getMockForAbstractClass('AnotherClass'); 
     $mock->expects($this->once()) 
      ->method('_loadCache'); 

     $mock->doAnotherStuff(); 
    } 
} 

Метод называется, но PHPUnit говорит, что это не так.

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

Редактировать Я wasn't объявляя свои методы с двойным двоеточием, это было только для обозначения, что это был публичный метод (интерфейс). Обновлено до полного объявления классов/методов.

Edit 2 Я должен сказать, что I'm тестирования некоторых реализаций метода в абстрактном классе (отредактированный код, чтобы отразить это). Поскольку я не могу создать экземпляр класса, как я могу проверить это?

Я размышляю над созданием SomeClassSimple, распространяя SomeClassAbstract и тестируя это вместо этого. Правильно ли это?

ответ

11

В версии PHPUnit, которая у меня есть, класс PHPUnit_Framework_MockObject_Mock использует PHP get_class_methods, чтобы определить интерфейс объекта, издевающегося. get_class_methods выберет только общедоступные методы, а не защищенные или частные.

Это соответствует духу модульного тестирования xUnit. Рассмотрим пример PHPUnit docs о том, как использовать Mock Objects. Там SUT составляет Subject, который имеет защищенный метод notify. Однако тестируемый метод равен doSomething. Учитывая Subject как черный ящик, нам все равно, как это реализовано. Однако мы позаботимся о его поведении. В частности, мы требуем, чтобы, когда мы вызываем doSomething, вызывается метод update любых прикрепленных наблюдателей. Итак, мы издеваемся над сторонним объектом Observer, и мы настроим математическое ожидание того, что будет вызываться его метод update. Обратите внимание, что хотя защищенный метод notify выполняется, он не указан явно в тесте. Это дает нам свободу вносить изменения в любое удобное для нас время, только пока сохраняется поведение.

Возврат к вашему примеру.

class MyTest extends PHPUnit_Framework_TestCase 
{ 
    public function testCalled() 
    { 
    $mock = $this->getMock('SomeClass'); 
    $mock->expects($this->once()) 
     ->method('_protectedMethod'); 

    $mock->doStuff(); 
    } 
} 

Здесь ваш метод testCalled не создает экземпляр тестируемой системы (SUT), который был бы SomeClass. Версия doStuff в mock от SomeClass ничего не делает. В частности, он не вызывает _protectedMethod.

+0

Спасибо вам Ewan, вы правы.Конечно, если его макетный объект, у него нет реализации метода. Как тупой я. Я буду отмечать ваш ответ как правильный. Если вы можете, пожалуйста, посмотрите мое второе редактирование. –

+0

Так что я пытаюсь проверить - это реализация, а не поведение, верно? Это немного более ясно для меня, спасибо. –

+2

Модульное тестирование, я думаю, больше искусства, чем кажется. Я высоко оценил это после прочтения тестовых шаблонов xUnit: рефакторинг тестового кода Жерара Мезароса. В свою очередь, книга была сильной рекомендацией Себастьяна Бергмана, автора PHPUnit. –

4

Похоже, вам просто нужно сказать PHPUnit, что вы хотите, чтобы дразнить из этой функции, т.е .:

$mock = $this->getMock('SomeClass', 
         array('_protectedMethod')); 
$mock->expects($this->once()) 
    ->method('_protectedMethod'); 

$mock->doStuff(); 
Смежные вопросы