Я относительно новый конвертер для модульного тестирования в целом, и я столкнулся с камнем преткновения здесь:
Как проверить код, который подключается и выполняет операции на удаленном FTP-сервере, используя встроенные в PHP функции ftp? Некоторый googling оказался быстрым издевательским вариантом для Java ( MockFtpServer ), но ничего не доступно для PHP.
У меня есть подозрение, что ответ может заключаться в создании класса-оболочки для PHP-функций ftp, которые впоследствии могут быть заглушены / изделены, чтобы имитировать успешные / неудачные операции ftp, но я бы очень признателен за вклад от людей, которые умнее меня!
Обратите внимание, что я работал с PHPUnit и нуждаюсь в помощи в этой структуре.
Согласно запросу от @hakre, упрощенный код, который я хочу протестировать, выглядит следующим образом. Я, по сути, прошу наилучшего способа проверить:
public function connect($conn_name, $opt=array()) { if ($this->ping($conn_name)) { return TRUE; } $r = FALSE; try { if ($this->conns[$conn_name] = ftp_connect($opt['host'])) { ftp_login($this->conns[$conn_name], $opt['user'], $opt['pass']); } $r = TRUE; } catch(FtpException $e) { // there was a problem with the ftp operation and the // custom error handler threw an exception } return $r; }
ОБНОВЛЕНИЕ / РЕШЕНИЕ РЕЗЮМЕ
Резюме проблемы
Я не был уверен, как тестировать методы в изоляции, которые требуют связи с удаленным FTP-сервером. Как вы должны тестировать возможность подключения к внешнему ресурсу, на который у вас нет контроля?
Резюме решения
Создайте класс адаптера для операций FTP (методы: connect, ping и т. Д.). Этот класс адаптера затем легко заглушается, чтобы возвращать определенные значения при тестировании другого кода, который использует адаптер для выполнения операций FTP.
ОБНОВЛЕНИЕ 2
Недавно я наткнулся на отличный трюк с использованием пространств имен в ваших тестах, что позволяет вам «фальсифицировать» встроенные функции PHP. Хотя адаптер был правильным способом в моем конкретном случае, это может быть полезно для других в подобных ситуациях:
Mocking php глобальные функции для модульного тестирования
Два подхода, которые приходят на ум:
Создайте два адаптера для вашего класса FTP:
«Макет», который фактически не подключается к чему-либо и возвращает только семенные данные.
Метод FTP connect()
теперь выглядит следующим образом:
public function connect($name, $opt=array()) { return $this->getAdapter()->connect($name, $opt); }
Макет адаптера может выглядеть примерно так:
class FTPMockAdapter implements IFTPAdapter { protected $_seeded = array(); public function connect($name, $opt=array()) { return $this->_seeded['connect'][serialize(compact('name', 'opt'))]; } public function seed($data, $method, $opt) { $this->_seeded[$method][serialize($opt)] = $data; } }
В своем тесте вы затем высеваете адаптер с результатом и убедитесь, что connect()
получает вызов соответствующим образом:
public function setUp( ) { $this->_adapter = new FTPMockAdapter(); $this->_fixture->setAdapter($this->_adapter); } /** This test is worthless for testing the FTP class, as it * basically only tests the mock adapter, but hopefully * it at least illustrates the idea at work. */ public function testConnect( ) { $name = '...'; $opt = array(...); $success = true // Seed the connection response to the adapter. $this->_adapter->seed($success, 'connect', compact('name', 'opt')); // Invoke the fixture's connect() method and make sure it invokes the // adapter properly. $this->assertEquals($success, $this->_fixture->connect($name, $opt), 'Expected connect() to connect to correct server.' ); }
В приведенном выше тестовом примере setUp()
вводит макет адаптера, чтобы тесты могли вызвать метод connect()
класса FTP, фактически не инициировав FTP-соединение. Затем тест семя адаптера получает результат, который будет возвращен только в том случае, если метод connect()
адаптера был вызван с правильными параметрами.
Преимущество этого метода заключается в том, что вы можете настроить поведение макетного объекта, и он сохраняет весь код в одном месте, если вам нужно использовать макет в нескольких тестовых случаях.
Недостатки этого метода заключаются в том, что вам нужно дублировать много функциональных возможностей, которые уже были построены (см. Подход № 2), и, возможно, вы уже представили другой класс, для которого вы должны писать тесты.
Альтернативой является использование фальшивой структуры PHPUnit для создания динамических макетных объектов в вашем тесте. Вам все равно придется вставлять адаптер, но вы можете его создать «на лету»:
public function setUp( ) { $this->_adapter = $this->getMock('FTPAdapter'); $this->_fixture->setAdapter($this->_adapter); } public function testConnect( ) { $name = '...'; $opt = array(...); $this->_adapter ->expects($this->once()) ->method('connect') ->with($this->equalTo($name), $this->equalTo($opt)) ->will($this->returnValue(true)); $this->assertTrue($this->_fixture->connect($name, $opt), 'Expected connect() to connect to correct server.' ); }
Обратите внимание, что вышеупомянутый тест извиняет адаптер для класса FTP, а не самого FTP-класса , поскольку это было бы довольно глупой задачей.
Такой подход имеет преимущества перед предыдущим подходом:
Однако есть некоторые недостатки этого подхода:
См. http://phpunit.de/manual/current/en/test-doubles.html#test-doubles.mock-objects для получения дополнительной информации.
Ваш подход, похоже, в порядке. Всегда есть ограничения на то, что вы можете тестировать на единицу, учитывая, что в конечном итоге вы будете находиться на низкоуровневых функциях, которые взаимодействуют напрямую с внешними.
Я бы рекомендовал вам настроить свой FTP на адаптерах, которые вы можете издеваться, а затем вы можете покрыть фактическое тестирование адаптера с помощью тестирования интеграции.
Хотя одним из вариантов было бы издеваться над FTP-сервером и подключаться к нему в тестах (вам нужно было бы только изменить конфигурацию ftp-сервера / конфигурацию подключения к макету сервера и не изменять какой-либо код), есть еще один: вы не необходимо выполнить модульные функции PHP.
Эти функции не являются компонентами, написанными вами, поэтому вы не должны их тестировать.
В противном случае вы можете начать писать тест, if
или даже такие операторы, как +
но вы этого не делаете.
Чтобы сказать больше, было бы хорошо видеть ваш код. Если у вас есть процедурный код стиля, сложно выполнить mock / stub / dupe каждую функцию FTP. На самом деле это не так легко с PHP.
Но если вместо этого вы создадите объект подключения FTP, который обертывает все эти функции, вы можете создать тестовый обход для этого объекта подключения к FTP. Это требует рефакторинга вашего кода.