Pull to refresh

Comments 31

Я думал PHP и TDD не уложатся в голове одного человека, оказывается может))
Когда на PHP с C++ переезжаешь, еще и не такие метаморфозы могут произойти ;-)
Зря вы так… «Имя нам — легион» :) Вроде все более-менее популярные фреймворки разрабатываются с использованием TDD (или покрытием тестами после написания кода, кто их знает). Жаль, что на рынке это не востребовано, что во фрилансе, что в вакансиях * PHP Developer. Про JQuery или ещё что непосредственно к PHP не относящееся обязательно напишут в требованиях, про PHPUnit — ни разу не видел.
Ничего, нужно верить в светлые времена! Будет и на нашей улице праздник!
Быстрее, наверное, рельсы научатся ставиться в продакшене одной строчкой :)
Хероку итак в одну строчку всё делет.
Ещё бы поднять его локально или на вдске в одну строку :)
У меня просто такой стереотип PHP'шников.
Автор статьи без сомнения — молодец!
UFO just landed and posted this here
Неправильно выразился. Не то, чтобы совсем не видел — видел, вон, например, сбоку висит. Не видел таких, чтобы и TDD требовалось, и я мог претендовать. Я-то как раз сайтостроитель, только по возможности практикую разные *DD. Но не востребовано оно как-то при найме сайтостроителей :( Вот сижу пост пишу о TDD на PHP — последние посты на эту тему показывают, что люди от абстрактных примеров не могут перейти к реальному использованию. Пишу с корыстной целью — чтобы не только в сложных бизнес- или энтерпрайс-решениях был востребован хороший код :)
UFO just landed and posted this here
Как обычно работают сайтостроители — берут CMS/CMF/фреймворк и пишут специфичную логику. И хорошо, если пишут её в рамках идеологии системы, не нарушая её архитектуру. Получается код CMS/CMF/фреймворка тестами покрыт, а специфичный код нет. Сначала код тривиальный и вроде бы тестирование не нужно, но постепенно разрастается, вставляются костыли, антипаттерны и тривиальный код превращается в код, который очень сложно поддерживать.
Мое имхо — не стоит заливать/перезаливать всю базу данных из нескольких десятков таблиц перед каждым тестом. Возможно есть смысл не вносить ключи в дамп вообще, и в каждом тесте вливать не всю базу, а только ту таблицу, которая относится к тестируемой сущьности. В таком случае и тесты быстрее отработают и целосность вы тестировать не будете(т.к. задача проверки корректности работы ключей — это задача mysql команды).
Таблицы перед каждым тестом содержат всего по 3-5 записей и заливаются всегда в пустую базу. Ключи я и так отфильтровываю при сравнении датасетов, за исключением некоторых тестов, где именно ключи и интересны.
Чтоб не заливать всю базу перед каждым тестом, можно (попробовать) заливать неизменяемую часть в методах setUpBeforeClass/tearDownAfterClass, а в setUp/tearDown чистить только нужные таблицы.
Спасибо за наводку! Нужно будет учесть, полезная фишка.
Вот собственно из-за столь неочевидных методов тестирования БД, я и написал Codeception. В нем тестирование БД проводится всего лишь подключением модуля Db, и указания файла с дампом. При использовании ОРМ в проекте, базу вообще можно не очищать, а выполнять тесты в транзакции, и откатывать их в конце.

Как по мне, то задача тут достаточно проста. А решается в PHPUnit она, как видно даже с этого поста, излишне сложно. И DbUnit это инструмент для усложнения жизни, а не эффективного тестирования. имхо.
Ваш Codeception в моем списке на Workflowy с первой Вашей статьи. Скоро я до него доберусь, выглядит он достаточно заманчиво.
В любом случае, лучше если есть возможность, вообще не заниматься очисткой БД в рамках юнит-тестов. Используйте транзакции. Учитывая, что в MySQL нет вложенных транзакций, то важно, чтобы у вас был какой-то слой абстракции над PDO, который мог бы вести учет транзакций и откатывать только самую последнюю из них.
Но ведь тестирование выполняется не на боевой базе. У нас вообще есть несколько баз с одинаковой структурой — для модульных тестов, для нагрузочных тестов и боевая. Каков в таком случае минус в том, что база очищается каждый раз?
Один из самых важных критериев для юнит-тестов это скорость выполнения. А очистка базы, скорее всего, будет самой медленной операцией. Например, на MageConf упоминали, что в Magento несколько тысяч юнит-тестов, которые выполняются за одну минуту. Такая скорость достигнута только за счет транзакций. Без них, сами понимаете, время выполнения будет гораздо длиннее.
Да, Вы правы, что для модульных тестов время выполнения очень важно. Хотя в нашем случае ему еще далеко до критической отметки. Нужно будет провести на следующей неделе профилирование, чтобы точно выяснить, какой процент времени занимает именно очистка базы. Просто достаточно удивительно, что в DbUnit этого не учли, если это действительно так. Но возможно, что это не учтено только в порте для PHP.
Честно, я не очень, разбирался в DbUnit, но скорее всего, для MySQL там хорошего решения нет. Ибо тут необходимы вложенные транзакции или их эмуляция. Эмулировать транзакции можно, если транзакциями занимается не PDO, а некая обертка над ним. Она может следить, является ли эта транзакция последней, и только тогда делать rollback или commit.

Такие вещи реализованы в Doctrine, Doctrine 2 DBAL. И не реализованы, в том же Zend_Db, хотя там достаточно простой патч и висит у них в багтрекере ещё с 2007го. Почему? Непонятно.
На самом деле, судя по состоянию кода, DbUnit для PHP сейчас еще очень далек от полноценной реализации. И не только для MySQL.
Здесь стоит помнить о том, что любые операции по модификации структуры БД в MySQL приводят к автокомиту транзакций. Так что и тесты необходимо будет проектировать соответственно.
Пробовал работать с 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.
Я пока обхожусь миграциями с версионностью. Также, постоянно в объекте миграций хранится массив данных, которые когда-либо были загружены и т.п. Таким образом убирается хардкод тестовых данных по типу $this->assertEquals($oModel->getName(3), 'Vasya');
Кстати — есть ещё несколько полезных методов, кроме setUp() и tearDown() — setUpBeforeClass() и т.д. Т.е. для всего теста можно загружать неизменяющиеся данные не на этапе подготовки тестов (что будет прекрасной утилизацией процессорного времени, но зря, если какой-либо тест упадёт до использования данных), а при инстанциировании класса теста.
ОМГ! Сначала expected, потом actual: $this->assertEquals('Vasya', $oModel->getName(3));
И assertEquals это не хардкод, это так тестировать результат принято:-)
Не знаю насчёт хардкода:
//...
$user = new User();
$user_name = 'Vasya';

$user->setName($user_name);

$this->assertEquals($user_name, $user->getName());
//...

Имхо так лучше, минимум меньше вероятность допустить ошибку в тесте.
смайлик в конце как бы намекает на неверно построенное предложение автором коммента:-)
Я не про assertEquals(). Я говорил вот про что:
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' — магическая переменная.
Sign up to leave a comment.

Articles