Comments 31
Я думал PHP и TDD не уложатся в голове одного человека, оказывается может))
-15
Когда на PHP с C++ переезжаешь, еще и не такие метаморфозы могут произойти ;-)
+2
Зря вы так… «Имя нам — легион» :) Вроде все более-менее популярные фреймворки разрабатываются с использованием TDD (или покрытием тестами после написания кода, кто их знает). Жаль, что на рынке это не востребовано, что во фрилансе, что в вакансиях * PHP Developer. Про JQuery или ещё что непосредственно к PHP не относящееся обязательно напишут в требованиях, про PHPUnit — ни разу не видел.
+4
Ничего, нужно верить в светлые времена! Будет и на нашей улице праздник!
+1
У меня просто такой стереотип PHP'шников.
Автор статьи без сомнения — молодец!
Автор статьи без сомнения — молодец!
-2
UFO just landed and posted this here
Неправильно выразился. Не то, чтобы совсем не видел — видел, вон, например, сбоку висит. Не видел таких, чтобы и TDD требовалось, и я мог претендовать. Я-то как раз сайтостроитель, только по возможности практикую разные *DD. Но не востребовано оно как-то при найме сайтостроителей :( Вот сижу пост пишу о TDD на PHP — последние посты на эту тему показывают, что люди от абстрактных примеров не могут перейти к реальному использованию. Пишу с корыстной целью — чтобы не только в сложных бизнес- или энтерпрайс-решениях был востребован хороший код :)
+2
UFO just landed and posted this here
Как обычно работают сайтостроители — берут CMS/CMF/фреймворк и пишут специфичную логику. И хорошо, если пишут её в рамках идеологии системы, не нарушая её архитектуру. Получается код CMS/CMF/фреймворка тестами покрыт, а специфичный код нет. Сначала код тривиальный и вроде бы тестирование не нужно, но постепенно разрастается, вставляются костыли, антипаттерны и тривиальный код превращается в код, который очень сложно поддерживать.
+2
Мое имхо — не стоит заливать/перезаливать всю базу данных из нескольких десятков таблиц перед каждым тестом. Возможно есть смысл не вносить ключи в дамп вообще, и в каждом тесте вливать не всю базу, а только ту таблицу, которая относится к тестируемой сущьности. В таком случае и тесты быстрее отработают и целосность вы тестировать не будете(т.к. задача проверки корректности работы ключей — это задача mysql команды).
+1
Таблицы перед каждым тестом содержат всего по 3-5 записей и заливаются всегда в пустую базу. Ключи я и так отфильтровываю при сравнении датасетов, за исключением некоторых тестов, где именно ключи и интересны.
0
Чтоб не заливать всю базу перед каждым тестом, можно (попробовать) заливать неизменяемую часть в методах setUpBeforeClass/tearDownAfterClass, а в setUp/tearDown чистить только нужные таблицы.
0
Вот собственно из-за столь неочевидных методов тестирования БД, я и написал Codeception. В нем тестирование БД проводится всего лишь подключением модуля Db, и указания файла с дампом. При использовании ОРМ в проекте, базу вообще можно не очищать, а выполнять тесты в транзакции, и откатывать их в конце.
Как по мне, то задача тут достаточно проста. А решается в PHPUnit она, как видно даже с этого поста, излишне сложно. И DbUnit это инструмент для усложнения жизни, а не эффективного тестирования. имхо.
Как по мне, то задача тут достаточно проста. А решается в PHPUnit она, как видно даже с этого поста, излишне сложно. И DbUnit это инструмент для усложнения жизни, а не эффективного тестирования. имхо.
+4
В любом случае, лучше если есть возможность, вообще не заниматься очисткой БД в рамках юнит-тестов. Используйте транзакции. Учитывая, что в MySQL нет вложенных транзакций, то важно, чтобы у вас был какой-то слой абстракции над PDO, который мог бы вести учет транзакций и откатывать только самую последнюю из них.
0
Но ведь тестирование выполняется не на боевой базе. У нас вообще есть несколько баз с одинаковой структурой — для модульных тестов, для нагрузочных тестов и боевая. Каков в таком случае минус в том, что база очищается каждый раз?
0
Один из самых важных критериев для юнит-тестов это скорость выполнения. А очистка базы, скорее всего, будет самой медленной операцией. Например, на MageConf упоминали, что в Magento несколько тысяч юнит-тестов, которые выполняются за одну минуту. Такая скорость достигнута только за счет транзакций. Без них, сами понимаете, время выполнения будет гораздо длиннее.
+1
Да, Вы правы, что для модульных тестов время выполнения очень важно. Хотя в нашем случае ему еще далеко до критической отметки. Нужно будет провести на следующей неделе профилирование, чтобы точно выяснить, какой процент времени занимает именно очистка базы. Просто достаточно удивительно, что в DbUnit этого не учли, если это действительно так. Но возможно, что это не учтено только в порте для PHP.
0
Честно, я не очень, разбирался в DbUnit, но скорее всего, для MySQL там хорошего решения нет. Ибо тут необходимы вложенные транзакции или их эмуляция. Эмулировать транзакции можно, если транзакциями занимается не PDO, а некая обертка над ним. Она может следить, является ли эта транзакция последней, и только тогда делать rollback или commit.
Такие вещи реализованы в Doctrine, Doctrine 2 DBAL. И не реализованы, в том же Zend_Db, хотя там достаточно простой патч и висит у них в багтрекере ещё с 2007го. Почему? Непонятно.
Такие вещи реализованы в Doctrine, Doctrine 2 DBAL. И не реализованы, в том же Zend_Db, хотя там достаточно простой патч и висит у них в багтрекере ещё с 2007го. Почему? Непонятно.
-1
На самом деле, судя по состоянию кода, DbUnit для PHP сейчас еще очень далек от полноценной реализации. И не только для MySQL.
+1
Здесь стоит помнить о том, что любые операции по модификации структуры БД в MySQL приводят к автокомиту транзакций. Так что и тесты необходимо будет проектировать соответственно.
+3
Пробовал работать с DBUnit, быстро разочаровался. Но так как тесты пишу лично для себя (читай — никто их у меня не просит и не требует, тестирую что хочу и как хочу), то быстро пришёл к такому способу написания функциональных/интеграционных тестов (тест с БД никак не может быть юнит-тестом ;) ): создаю мокостабы PDO, mysqli или своей примитивной обёртки над mysql_*) для методов типа $db->query($query) и $db->fetch_assoc($result), проверяющие, что тестируемый метод действительно вызывает, например, сначала $db->query('SELECT * FROM users WHERE group_id = 1'), а потом три раза $db->fetch_assoc(), и возвращающие true(не false), array(...), array(...) и false.
0
Я пока обхожусь миграциями с версионностью. Также, постоянно в объекте миграций хранится массив данных, которые когда-либо были загружены и т.п. Таким образом убирается хардкод тестовых данных по типу $this->assertEquals($oModel->getName(3), 'Vasya');
Кстати — есть ещё несколько полезных методов, кроме setUp() и tearDown() — setUpBeforeClass() и т.д. Т.е. для всего теста можно загружать неизменяющиеся данные не на этапе подготовки тестов (что будет прекрасной утилизацией процессорного времени, но зря, если какой-либо тест упадёт до использования данных), а при инстанциировании класса теста.
Кстати — есть ещё несколько полезных методов, кроме setUp() и tearDown() — setUpBeforeClass() и т.д. Т.е. для всего теста можно загружать неизменяющиеся данные не на этапе подготовки тестов (что будет прекрасной утилизацией процессорного времени, но зря, если какой-либо тест упадёт до использования данных), а при инстанциировании класса теста.
-2
ОМГ! Сначала expected, потом actual: $this->assertEquals('Vasya', $oModel->getName(3));
И assertEquals это не хардкод, это так тестировать результат принято:-)
И assertEquals это не хардкод, это так тестировать результат принято:-)
+2
Не знаю насчёт хардкода:
Имхо так лучше, минимум меньше вероятность допустить ошибку в тесте.
//...
$user = new User();
$user_name = 'Vasya';
$user->setName($user_name);
$this->assertEquals($user_name, $user->getName());
//...
Имхо так лучше, минимум меньше вероятность допустить ошибку в тесте.
0
Я не про assertEquals(). Я говорил вот про что:
При этом, таблица users будет уже заполнена нужными данными из миграции и при изменении тестового набора данных этот тест упадёт. Не каждый объект снабжён сеттерами и делать из мне не интересно, если я тестирую ЗАГРУЗКУ и ПОДГОТОВКУ данных объекта. Сеттеры тестировать конечно надо, но не в первую очередь. Я не об этом говорил.
В Вашем (и многоуважаемого VoICh) случае 'Vasya' — магическая переменная.
class UserTest extends PHPUnit_Framework_TestCase {
public static function setUpBeforeClass() {
DB_Migrate::load()->users;
}
public function testGetUser() {
$oUser = new UserModel(1);
$this->assertEquals(DB_Migrate::get()->users[1]['name'], $oUser->getName(), 'User load failed');
// Вместо
// $this->assertEquals('Vasya', $oUser->getName(), 'User load failed');
}
}
При этом, таблица users будет уже заполнена нужными данными из миграции и при изменении тестового набора данных этот тест упадёт. Не каждый объект снабжён сеттерами и делать из мне не интересно, если я тестирую ЗАГРУЗКУ и ПОДГОТОВКУ данных объекта. Сеттеры тестировать конечно надо, но не в первую очередь. Я не об этом говорил.
В Вашем (и многоуважаемого VoICh) случае 'Vasya' — магическая переменная.
+1
Sign up to leave a comment.
Articles
Change theme settings
Много текста про практику работы с PHPUnit/DbUnit