Pull to refresh

Comments 28

Вариант с копированием структуры базы напротив, требует много времени на свое выполнение, но зато и выполняется один раз. Т.е. Время его работы не зависит от количества запускаемых тестов.


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

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

Хотелось бы услышать с какими проблемами сталкивались Вы при использовании DbUnit и как их решали.


Со всеми вышеописанными. Решал так:
codeception.com/docs/08-Data

Т.е. решали поднимая каждый дамп для каждого юнит теста? А как тогда решали скорость выполнения тестов? При таком подходе у меня тесты выполнялись около 2-х минут, пришлось отказаться в пользу обычного TRUNCATE и скорость стала приемлемой: несколько десятков секунд.
Ну если вам нужна пустая база, возможно truncate помогает. Но лично я решал проблему очистки сразу с наполнением тестовыми данными, и потому что влив дампа, что truncate + заполнение данными занимали примерно одно и то же время.

А решения такие:
— эмулировать вложенные транзакции в приложении. Не так сложно, если не использовать чистый PDO, а какую-то обертку над ним.
— использовать SQLite.
— не использовать транзакции в тестируемом коде.

Но по скорости альтернативы транзакциям нет.
А вы в приложении всегда сейвпоинтами пользуетесь? Я всегда только commit/rollback.
почему всегда? коміті и роллбеки использується только на внешней транзакции, внутри транзакция стартует и откатівается уже сейвпойнтами
Да. Но это значит, что всегда в програмном коде нужно использовать сейвпоинты.
Можно конечно, но лучше исходить из универсального предположения, что мы должны тестировать любой код.
Это не вложенные (nested transaction) транзакции по определению.
Что тогда мешает им быть вложенными транзакциями?
Как минимум синтаксис.
Да, Вы правы. Транзакционный способ подходит к сожалению не для всех тестов. Добавлю эти ограничения в статью.
Я даже боюсь при слишком большом количестве операций с базой данных в одном тесте можно наткнуться на баг «Lock wait timeout exceeded».
Но не одним мускулем живы…
Не модульные это тесты, а функциональные.

Для себя проблему решил так:
— В модульных тестах, которые запускаются каждые «5 минут», проверяю работу классов с БД стабированием/моканием объектов PDO, mysqli, ORM или своей обёртки для mysql_*. Многословно получается (при использовании PHPUnit), но не проверяю ничего лишнего, в частности работу расширений PHP и СУБД. Грубо говоря, проверяю что вызван метод $db_con->query('SELECT * FROM users') и подставляю в возврат ожидаемый массив. То есть проверяю работу PHP кода и ничего больше.
— Проверку синтаксиса (прежде всего, судя по количеству фейлов) и логики отдельных запросов проверяю в интеграционных, которые запускаются гораздо реже.
— «Глобальную» работу с БД (несколько запросов, до десятков на страницу) с переходами между страницами проверяю в функциональных/приемочных тестах, которые запускаются крайне редко.

При таком подходе мудрить с оптимизацией времени выполнения тестов с СУБД не приходится.
Забыли сказать, что для не транзакционного движка вся эта песня с откатами работать не будет.
Идея плавает на поверхности: завести для тестов еще одну базу данных. Но в этом случае придется поддерживать в актуальном состоянии обе базы данных.

И что же, вы запускаете тесты на продакшн базе?
По топику видно, что речь о test и dev базах.
Используя расширение DBUnit следует переопределить метод getConnetction, который, как следует из ...

Опечаточка)
Не понимаю людей, которые не путешествуют по России при первой же возможности пытаются тестировать классы, работающие с базой данных. Ну зачем?
PHPUnit создан для тестирования интерфейсов! Я вообще не считаю, что он должен поддерживать тестирование БД.
Ну предположим, будет написан класс, задача которого отправлять запросы к БД используя переданное ему соединение с БД. Ну напишу я запрос, который передам методу этого класса. Протестирую, что с той пачки данных, которая имеется в тестовом окружении, мне вернулись правильные данные. А дальше то что? На живом сайте будет куча данных, других данных и этот тест толком не гарантирует того, что код или запрос рабочий. Индекса не будет, запрос будет тормозить, PHPUnit никогда это не протестирует. Нет, можно, наверное, навставлять костылей, которые и время выполнения запроса потестируют и explain запроса посмотрят, но это уже что-то сложное и не вообразимое в итоге получится.
Есть интерфейс для получения 5 последних вопрос из базы данных. И как тестировать без базы данных? Или не тестировать вообще? Последнее это уже признак плохого тестирования.
Не тестировать вообще не нужно. Покрывать тестами 100% кода тоже не нужно.

Если я правильно понял суть вопроса, то откройте для себя моки: www.phpunit.de/manual/3.0/en/mock-objects.html

Мок будет подменять экземпляр класса, который общается с БД. А тестировать нужно класс, который возвращает 5 последних вопросов в зависимости от того, что вернула БД, которая в тесте на самом деле мок.
Я знаю, что такое моки. Покрывать тестами 100% кода не нужно, но к этому надо стремиться. Вопрос остается в силе. «экземпляр класса, который общается с БД. » и который будет подменяться моком, вы предлагаете не тестировать?
Модульными тестами не тестировать, функциональными (фактически их и описывают в топике) много не натестируешь. Как например протестировать, что на сервер mysql упало бетонное перекрытие?

Класс, который возвращает 5 последних вопросов и класс, который общается с БД — в моём понимании это два разных класса. Это я про то, что в предыдущем комментарии было: «Есть интерфейс для получения 5 последних вопрос из базы данных.»
Один или два не так важно. Все же один класс останется без тестов?
Да, тот класс, который общается с БД, не будет протестирован.
Два класса важно, потому что один не будет иметь внешних зависимостей, а только взаимодействовать с интерфейсом другого класса, а значит может быть полностью протестирован при условии написания тестируемого кода естественно.
Я уж лучше протестирую оба класса. Потери в скорости минимальны кстати.
В общем-то без конкретных примеров кода обсуждать бесполезно. Если очень хочется — то можно всё, но сам я не буду :)
Порой проще использовать тестовые данные и БД чем создавать моки для классов баз данных.
Ибо там такие моки получатся, которые будут включать в себя парсер SQL. Зачем оно надо?
KISS
У меня не получалось моков, которым нужно парсить SQL… Что я делаю не так? :)
Тестирование БД недопустимо именно модульными тестами, потому что они должны выполняться максимально быстро. По крайней мере в TDD и ему подобных.
Ещё вот про dataproviders: www.phpunit.de/manual/3.6/en/writing-tests-for-phpunit.html#writing-tests-for-phpunit.data-providers. Это если сложности с возвратом кучи разных данных, которые могла бы вернуть БД.

Ну и, если всё таки очень хочется потестировать БД, то стоит почитать эту часть мануала: www.phpunit.de/manual/3.6/en/database.html. Там про датасеты и дататэйблы очень интересно написано.
Естественно, всё зависит от архитектуры. У вас не получилсоь, у кого-то получилось )

А тесты в транзакции выполняются максимально быстро. Проверьте.
Sign up to leave a comment.

Articles