В предыдущей статье я обсуждал, как вы можете создать более надежный код, который запрашивает вашу реляционную базу данных посредством автоматического тестирования. Но, как вы знаете, запросы — это только одна часть картины. Есть и другие операции с базой данных, а именно создание, обновление и удаление. Точно так же как поиск, они также должны быть проверены.
В этой статье я буду изучать другие возможности расширений баз данных PHPUnit, а также буду расширять свои знания по тестированию баз данных в PHP. В частности, целью является:
- Узнайте, как проверить результат операции вставки
- Используйте 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