Статьи

Больше пуленепробиваемости с расширением базы данных PHPUnit

В предыдущей статье я обсуждал, как вы можете создать более надежный код, который запрашивает вашу реляционную базу данных посредством автоматического тестирования. Но, как вы знаете, запросы — это только одна часть картины. Есть и другие операции с базой данных, а именно создание, обновление и удаление. Точно так же как поиск, они также должны быть проверены.

В этой статье я буду изучать другие возможности расширений баз данных PHPUnit, а также буду расширять свои знания по тестированию баз данных в PHP. В частности, целью является:

  1. Узнайте, как проверить результат операции вставки
  2. Используйте API утверждения базы данных PHPUnit для проверки работы

Я написал эту статью как самостоятельную, чтобы вам не приходилось читать мой первый учебник заранее, но если вы новичок в разработке через тестирование, я настоятельно рекомендую вам прочитать ее, а также введение в PHPUnit от Мишель Saver.

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

Настройка

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

В первом приложении статей не было реализации для сохранения статьи в базе данных, и эту функциональность мы собираемся протестировать. Предположим, что уже существует веб-интерфейс, где наши редакторы могут писать свои статьи, и это будет код вызова, который сохраняет статью в базе данных. Единственное, чего не хватает, — это самой реализации и, конечно же, теста. Делая это предположение, мы можем выделить поведение, которое мы хотим проверить.

Первый шаг — использовать дамп базы данных, включенный в исходный код, для создания базы данных в MySQL. Вот с высоты птичьего полета:

Следующим шагом, поскольку мы пишем тест базы данных, является настройка данных. В конце концов, нам нужно что-то для тестирования операций! Создайте файл XML с именем seed.xml , с dataset корневых элементов и некоторыми seed.xml данных. Например, вот как будет выглядеть файл:

 <dataset> <table name="sections"> <column>id</column> <column>name</column> <row> <value>1</value> <value>PHP</value> </row> </table> <table name="articles"> <column>id</columm> ... </table> </dataset> 

Я рекомендую вам писать свои собственные данные только для практики, хотя вы всегда можете использовать образец набора данных в репозитории GitHub. Все таблицы там уже заполнены.

Написание тестового класса

Каждый раз, когда вы запускаете тест, вам нужно очистить и вставить исходные данные в базу данных. Это делается путем переопределения некоторых методов в классе, который расширяет PHPUnit_Extensions_Database_TestCase .

 <?php class ArticleDaoTest extends PHPUnit_Extensions_Database_TestCase { public function getConnection() { $pdo = new PDO("mysql:host=localhost;dbname=bulletproof", "root", "password"); return $this->createDefaultDBConnection($pdo, "bulletproof"); } public function getSetUpOperation() { // whether you want cascading truncates // set false if unsure $cascadeTruncates = false; return new PHPUnit_Extensions_Database_Operation_Composite(array( new PHPUnit_Extensions_Database_Operation_MySQL55Truncate($cascadeTruncates), PHPUnit_Extensions_Database_Operation_Factory::INSERT() )); } public function getDataSet() { return $this->createXMLDataSet("seed.xml"); } } 

В приведенном выше коде мы переопределили метод getConnection() чтобы указать тестовому классу подключиться к базе данных, а затем получить тестовые данные из возврата метода getDataSet() который мы также переопределили.

Каждый тест будет seed.xml таблицы и seed.xml их, используя seed.xml для повторной инициализации базы данных. Если вы используете MySQL 5.5 или выше, вы можете столкнуться с проблемой, так как команда TRUNCATE недопустима для таблиц InnoDB, которые используют ограничения внешнего ключа. Чтобы обойти это ограничение, нам нужно переопределить его getSetUpOperation() .

Класс PHPUnit_Extensions_Database_Operation_MySQL55Truncate ссылается getSetUpOperation() является пользовательским подклассом PHPUnit_Extensions_Database_Operation_Truncate который переопределяет метод execute() :

 <?php class PHPUnit_Extensions_Database_Operation_MySQL55Truncate extends PHPUnit_Extensions_Database_Operation_Truncate { public function execute(PHPUnit_Extensions_Database_DB_IDatabaseConnection $connection, PHPUnit_Extensions_Database_DataSet_IDataSet $dataSet) { $connection->getConnection()->query("SET @PHAKE_PREV_foreign_key_checks = @@foreign_key_checks"); $connection->getConnection()->query("SET foreign_key_checks = 0"); parent::execute($connection, $dataSet); $connection->getConnection()->query("SET foreign_key_checks = @PHAKE_PREV_foreign_key_checks"); } } 

Как бы страшно это не выглядело, метод execute() просто запоминает настройку внешних ключей MySQL, временно отключает ее, чтобы можно было продолжить выполнение команды TRUNCATE , а затем восстанавливает настройку.

Написание вашего теста

Теперь пришло время испачкать руки, написав сам тест. Предположим, мы согласны со следующим обновлением для интерфейса IArticleDao :

 <?php interface IArticleDao { // here is the new method for saving public function save($article); // already implemented in the first article public function getArticles($sectionId); } 

Первый тест, который нужно написать — это тест на сохранение статьи. Следует убедиться, что данные статьи попадают в правильные столбцы таблицы articles .

 <?php class ArticleDaoTest extends PHPUnit_Extension_Database_TestCase { ... public function testSaveArticle() { $article = new ArticleDAO(); $article->save(array( "title" => "PHP is Great!", ... )); $resultingTable = $this->getConnection() ->createQueryTable("articles", "SELECT * FROM articles"); $expectedTable = $this->createXmlDataSet( "expectedArticles.xml") ->getTable("articles"); $this->assertTablesEqual($expectedTable, $resultingTable); } } 

Сначала мы создаем экземпляр ArticleDAO а затем вызываем новый метод save() который мы тестируем, и передаем ассоциативный массив, представляющий статью. После того, как мы сохранили статью, мы хотим проверить, правильно ли сохранены данные. Мы запрашиваем содержимое таблицы и QueryTable их в экземпляр QueryTable .

С результатами нашей операции save() в $resultingTable мы должны сравнить ее с ожидаемым набором данных. Но где мы можем получить этот ожидаемый набор данных? От метода createXmlDataSet() и XML-файла expectedArticles.xml createXmlDataSet() .

Метод getTable() указывает, что мы хотим получить только содержимое таблицы articles так как именно эту мы хотим сравнить с нашей $resultingTable .

Затем последним шагом будет сравнение assertTablesEqual() и $expectedTable assertTablesEqual() что выполняется путем подачи обеих переменных в метод assertTablesEqual() .

Настройка набора данных в expectedArticles.xml seed.xml такая же, как наш seed.xml ; вот пример:

 <dataset> <table name="articles"> <column>id</column> <column>title</column> ... <row> <value>1</value> <value>PHP is Great!</value> ... </row> </table> </dataset> 

Прохождение тестов

В тестируемой разработке отказ является первым шагом. Если вы запустите тест сейчас, он потерпит неудачу, потому что мы еще не написали реализацию.

  jeune @ Miakka: ~ / пуленепробиваемый $ phpunit ArticleTest.php
 PHPUnit 3.6.7 от Себастьяна Бергмана.

 Неустранимая ошибка PHP: класс ArticleDAO содержит 1 абстрактный метод и поэтому должен быть объявлен как абстрактный или реализовать оставшиеся методы (IArticleDAO :: save) в /bulletproofing/ArticleDao.php в строке 12 

Следующим шагом является написание кода, который позволит нам пройти тест (то есть сохранить статью в базе данных). Давайте реализуем метод, который был добавлен в наш интерфейс IArticleDAO .

 <php class ArticleDAO implements IArticleDAO { ... public function save($article) { $db = new PDO( "mysql:host=localhost;dbname=bulletproof", "root", "password"); $stmt = $db->prepare("INSERT INTO articles (title, description, content, preview_image, section_id) value (:title, :description, :content, :preview_image, :section_id)"); $stmt->execute($article); return true; } } 

Теперь, если мы снова запустим что-то, мы должны увидеть прохождение теста.

  jeune @ Miakka: ~ / Пуленепробиваемый $ phpunit ArticleTest.php
 PHPUnit 3.6.7 от Себастьяна Бергмана.

 ,

 Время: 0 секунд, память: 3.00Mb

 ОК (1 тест, 1 утверждение) 

Резюме

В этом уроке мы увидели, как написать тест базы данных для операции вставки в PHP, используя расширение базы данных PHPUnit. Техника, представленная в этом руководстве, также должна работать для операций удаления и обновления.

Изображение через Fotolia