Макет объектов в PHPUnit для эмуляции статических вызовов методов? - PullRequest
4 голосов
/ 05 декабря 2008

Я пытаюсь протестировать класс, который управляет доступом к данным в базе данных (по сути, CRUD). Используемая нами библиотека БД имеет API, в котором вы сначала получаете объект таблицы статическим вызовом:

function getFoo($id) {
  $MyTableRepresentation = DB_DataObject::factory("mytable");
  $MyTableRepresentation->get($id);
  ... do some stuff
  return $somedata
}

... вы поняли.

Мы пытаемся протестировать этот метод, но высмеиваем вещи DataObject, чтобы (а) нам не требовалось фактическое соединение с БД для теста, и (б) нам даже не нужно было включать библиотеку DB_DataObject для теста.

Однако в PHPUnit я не могу получить $ this-> getMock () для правильной установки статического вызова. У меня есть ...

        $DB_DataObject = $this->getMock('DB_DataObject', array('factory'));

... но тест все равно говорит неизвестный метод "фабричный". Я знаю, что это создает объект, потому что прежде чем он сказал, что не может найти DB_DataObject. Теперь это возможно. Но нет способа?

Что я действительно хочу сделать, так это получить два фиктивных объекта, один для объекта таблицы. Таким образом, мне нужно не только указать, что фабрика является статическим вызовом, но и чтобы она возвращала какой-то заданный другой фиктивный объект, который я уже настроил.

В качестве предупреждения я должен упомянуть, что я делал это в SimpleTest некоторое время назад (не могу найти код), и он работал нормально.

Что дает?

[UPDATE]

Я начинаю понимать, что это как-то связано с ожидаемым ()

Ответы [ 6 ]

2 голосов
/ 20 июля 2010

Если вы не можете изменить библиотеку, измените свой доступ к ней. Измените все вызовы DB_DataObject :: factory () для метода экземпляра в вашем коде:

function getFoo($id) {
  $MyTableRepresentation = $this->getTable("mytable");
  $MyTableRepresentation->get($id);
  ... do some stuff
  return $somedata
}

function getTable($table) {
  return DB_DataObject::factory($table);
}

Теперь вы можете использовать частичное макетирование класса, который вы тестируете, и заставить getTable () возвращать объект фиктивной таблицы.

function testMyTable() {
  $dao = $this->getMock('MyTableDao', array('getMock'));
  $table = $this->getMock('DB_DataObject', ...);
  $dao->expects($this->any())
      ->method('getTable')
      ->with('mytable')
      ->will($this->returnValue($table));
  $table->expects...
  ...test...
}
2 голосов
/ 06 декабря 2008

Я согласен с вами обоими, что было бы лучше не использовать статический вызов. Однако, я думаю, я забыл упомянуть, что DB_DataObject - это сторонняя библиотека, и статический вызов - их лучшая практика для использования их кода, а не наша. Существуют и другие способы использования их объектов, которые включают непосредственное конструирование возвращаемого объекта. Он просто оставляет эти проклятые операторы include / require в любом файле класса, который использует этот класс DB_DO. Это отстой, потому что тесты сломаются (или просто не будут изолированы), если вы тем временем пытаетесь смоделировать класс с тем же именем в своем тесте - по крайней мере, я думаю.

1 голос
/ 05 декабря 2008

Это хороший пример зависимости в вашем коде - дизайн сделал невозможным внедрение в Mock, а не в реальном классе.

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

0 голосов
/ 10 июня 2011

С расширением PHPUnit MockFunction плюс runkit вы также можете имитировать статические методы. Будьте осторожны, потому что это исправление обезьяны и поэтому должно использоваться только в крайних случаях. Не заменяет собой хорошие практики программирования.

https://github.com/tcz/phpunit-mockfunction

0 голосов
/ 09 декабря 2008

Требуется ли / включает ли вы файл класса для DB_DataObject в вашем тестовом примере? Если класс не существует до того, как PHPUnit попытается смоделировать объект, вы можете получить ошибки, подобные этой.

0 голосов
/ 05 декабря 2008

Чего не хватает (или нет?) В вашем классе DB_DataObject является установщик для передачи подготовленного объекта БД перед вызовом метода фабрики. Таким образом, вы можете передать макет или пользовательский объект БД (с тем же интерфейсом) в случае необходимости.

В вашей настройке теста:

 public function setUp() {
      $mockDb = new MockDb();
      DB_DataObject::setAdapter($mockDb);
 }

Метод factory () должен возвращать проверенный экземпляр БД. Если он еще не интегрирован в ваш класс, вам, вероятно, придется также выполнить рефакторинг метода factory (), чтобы он работал.

...