Статьи

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

Здесь уже есть отличная статья, в которой обсуждается разработка через тестирование , но знаете ли вы, что вы также можете протестировать код, взаимодействующий с вашей базой данных? Особенно, если ваше приложение интенсивно использует данные, проверка кода CRUD на батарею тестов является хорошей практикой, которая помогает гарантировать, что ваше приложение работает правильно.

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

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

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

Разогрев

Пора намочить ноги! Это означает, что, как и в любом программном проекте, мы сначала изучим, что будем программировать.

Представьте, что вы серверный ниндзя онлайн-журнала, и вы только что получили спецификацию для нового журнала, который вы будете создавать:

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

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

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

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

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

 <dataset> <table name="sections"> <column>id</column> <column>name</column> <row> <value>1</value> <value>Gadgets</value> </row> </table> <table name="articles"> <column>id</column> <column>title</column> <column>description</column> <column>content</column> <column>preview_image</column> <column>section_id</column> <row> <value>1</value> <value>Android vs iOS</value> <value>Lorem ipsum dolor sit amet</value> <value>Aliquam scelerisque rhoncus porttitor. Nullam eget pulvinar magna. In vel lectus ut diam adipiscing porta vitae id nisi.</value> <value>android.jpg</value> <value>1</value> </row> ... </table> </dataset> 

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

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

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

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

 <?php require_once "PHPUnit/Autoload.php"; class ArticleDAOTest extends PHPUnit_Extensions_Database_TestCase { public function getConnection() { $db = new PDO( "mysql:host=localhost;dbname=bulletproof", "root", "password"); return $this->createDefaultDBConnection($db, "bulletproof"); } public function getDataSet() { return $this->createXMLDataSet("seed.xml"); } ... } 

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

 <?php interface IArticleDAO { public function getArticles($sectionId, $isHome); } 

Первый тест для написания тоже самый простой. То есть мы сначала кодируем случай, когда раздел не является домашней страницей. Мы ожидаем получить статьи, которые относятся только к этому разделу. Согласно моему файлу seed.xml , одним из таких разделов является раздел Gadgets. Он имеет id 1 и имеет следующие статьи:

  • Android против iOS
  • Android против Wp7
  • iOS 5

Таким образом, когда мы получим все статьи для этого раздела, вышеуказанные статьи должны быть возвращены. Это отражено в тесте, как показано ниже (для краткости я не указал другие атрибуты).

 <?php require_once "PHPUnit/Autoload.php"; require_once "ArticleDAO.php"; class ArticleDAOTest extends PHPUnit_Extensions_Database_TestCase { ... public function testGetArticlesNonHome() { $articleDAO = new ArticleDAO(); $articles = $articleDAO->getArticles(1, false); $this->assertEquals( array( array("id" => 1, "title" => "Android vs iOS"), array("id" => 2, "title" => "Android vs Wp7"), array("id" => 3, "title" => "iOS 5")), $articles); } ... } 

Метод testGetArticlesNonHome() сначала создает новый экземпляр объекта доступа к данным статьи (DAO) так же, как если бы он уже использовался в рабочем коде. DAO отвечает за инкапсуляцию и абстрагирование вызовов к базе данных. Затем getArticles() метод getArticles() DAO. Это метод, который мы хотим протестировать, поэтому результаты помещаются в переменную $articles article для проверки.

Как только мы получим результаты вызова метода в $articles , они сравниваются с ожидаемыми результатами. В этом случае мы ожидаем массив из трех статей, которые есть в разделе гаджетов. Удобный метод assertEquals() который поставляется с PHPUnit, используется для сравнения равенства.

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

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

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

 Неустранимая ошибка PHP: класс «ArticleDAO» не найден в /home/jeune/ArticleTest/ArticleDAOTest.php в строке 18
 Трассировка стека PHP:
 ... 

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

 <?php class ArticleDAO implements IArticleDAO { public function getArticles($sectionId, $isHome) { $db = new PDO( "mysql:host=localhost;dbname=bulletproof", "root", "password"); $result = $db->query("SELECT a.id, a.title FROM features f LEFT JOIN articles a ON f.article_id = a.id AND f.section_id = 1"); $articles = array(); while ($row = $result->fetch(PDO::FETCH_ASSOC)) { $articles[] = $row; } $result->closeCursor(); return $articles; } } 

Не забудьте включить вышеуказанный класс в файл тестового класса.

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

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

 ,

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

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

Исправление ошибок

Вы прошли тестирование, зафиксировали свой код и отправили его для проверки качества. Ты похлопываешь себя по спине и начинаешь суетиться на 9gag . Но подождите, QA обнаруживает ошибку!

Если в разделе нет статей, он возвращает статьи из ниоткуда! Для подтверждения вам нужно написать свой seed.xml чтобы иметь раздел без статей.

 <dataset> <table name="sections"> <column>id</column> <column>name</column> <row> <value>1</value> <value>Gadgets</value> </row> <row> <value>2</value> <value>Programming</value> </row> </table> ... <dataset> 

Учет новой ошибки, написав новый тест для случая, когда в разделе нет статей:

 <?php class ArticleDAOTest extends PHPUnit_Extensions_Database_TestCase { ... public function testGetArticlesNonHomeNoArticles() { $articleDAO = new ArticleDAO(); $articles = $articleDAO->getArticles(2, false); $this->assertEquals(array(), $articles); } ... } 

Правда, реализация завершается неудачно, когда мы запускаем тест:

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

 F.

 Время: 0 секунд, память: 6,75 Мб

 Был 1 сбой:
 1) ArticleDAOTest :: testGetArticlesNonHomeNoArticles
 Не удалось утверждать, что два массива равны.
 --- Ожидается
 +++ Actual
 @@ @@
  Array (
 + 0 => Массив (...)
 + 1 => Массив (...)
 + 2 => Массив (...)
  )

 /home/jeune/ArticleTest/ArticleDAOTest.php:33

 ОТКАЗЫ!
 Тесты: 2, Утверждения: 2, Неудачи: 1. 

Сообщение об ошибке говорит, что мы ожидали массив без содержимого, но вызов метода вернул массив с содержимым. Это явно ошибка! Попробуйте пройти новый тест, исправив код.

Резюме

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

Изображение через Татьяну Попову / Shutterstock