Имитировать объекты в PHPUnit для имитации вызовов статических методов?

Я пытаюсь протестировать класс, который управляет доступом к данным в базе данных (по сути, 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. Теперь это возможно. Но нет метода?

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

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

Что дает?

[ОБНОВИТЬ]

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

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Symfony Station Communiqué - 7 июля 2023 г
Symfony Station Communiqué - 7 июля 2023 г
Это коммюнике первоначально появилось на Symfony Station .
Оживление вашего приложения Laravel: Понимание режима обслуживания
Оживление вашего приложения Laravel: Понимание режима обслуживания
Здравствуйте, разработчики! В сегодняшней статье мы рассмотрим важный аспект управления приложениями, который часто упускается из виду в суете...
Установка и настройка Nginx и PHP на Ubuntu-сервере
Установка и настройка Nginx и PHP на Ubuntu-сервере
В этот раз я сделаю руководство по установке и настройке nginx и php на Ubuntu OS.
Коллекции в Laravel более простым способом
Коллекции в Laravel более простым способом
Привет, читатели, сегодня мы узнаем о коллекциях. В Laravel коллекции - это способ манипулировать массивами и играть с массивами данных. Благодаря...
Как установить PHP на Mac
Как установить PHP на Mac
PHP - это популярный язык программирования, который используется для разработки веб-приложений. Если вы используете Mac и хотите разрабатывать...
4
0
7 487
6
Перейти к ответу Данный вопрос помечен как решенный

Ответы 6

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

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

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

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

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

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

Ответ принят как подходящий

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

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

Если вы не можете изменить библиотеку, измените доступ к ней. Реорганизуйте все вызовы 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...
}

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

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

Другие вопросы по теме