Новый подход к тестированию Zend Framework/Doctrine приложения: пусть тестируют пользователи!

Когда проект растет, то, как правило, растет и его кодовая база, количество модулей, да и число программистов, вовлечённых в проект. Постепенно от закачки файлов по ftp (если такое было 🙂 команда переходит к Subversion/Git репозитарию, тикет-системам и начинает использовать инструменты для увеличения производительности совместной работы. Важным вопросом становится тестирование, т.к. в больших веб-приложениях компоненты так или иначе зависят друг от друга, то очень часто можно наблюдать ситуацию, когда программист прикрутил какую-нибудь фишку в одной части системы, а в другой что-то отвалилось. Некоторые тут скажут, что надо делать другую архитектуру системы, чтобы компоненты были связаны как можно меньше. Но во-первых, они всё равно будут связаны, а во-вторых может возникнуть дублирование кода. В любом случае, меньшая связанность модулей – это то, к чему надо стремиться.

Чтобы решить проблему тестирования обычно применяются автотесты, но для написания тестов надо тратить уйму времени, которого конечно нет. Поэтому либо тесты есть, либо тестов нет. У нас создание автотестов – это притча во языцах, постоянно хотим внедрить, но слишком часто меняется код, и автотесты очень быстро устаревали бы. Поэтому создание юнит-тестов мы решили отложить до момента, когда система полностью устоится, чтобы не переписывать их по сто раз. А тестить то всё равно надо.

Один программист из нашей компании как-то сделал очень хорошую штуку, когда возбуждается исключение в коде на продакшен сервере, то на общий email отправляется письмо с сообщением об ошибке. Поэтому, можно узнавать, чем дышит сервер. Но исключения возбуждались не всегда. Больше всего проблем было с тем, что у нас падал какой-нибудь DQL (Doctrine Query Language) запрос к базе в контроллере/модели/сервисе, и в результате вид (представление) не получал каких-либо данных. Но т.к. падал запрос, то до рендеринга вида управление даже не доходило, и мы не могли узнать об этих ошибках. Встала задача сделать так, чтобы исключения Doctrine всплылвали в Zend Framework, а там уже перехватывались и обрабатывались бы контроллером ошибок (см. ErrorController).

Я долго копался в коде Doctrine, искал всяческие throw Exception, try/catch блоки. Я узнал, что все исключения в Doctrine наследуются от класса Doctrine_Exception. Уже думал отнаследоваться от него и в потомке переопределить функцию отображения ошибки на экране, когда наткнулся на параметр  ATTR_THROW_EXCEPTIONS. Следующий код в ZendExtra_Resource_Doctrine устанавливает всплытие ошибок из Doctrine в Zend Frakework:

Теперь при возникновении ошибки,  связанной с запросом, она всплывет в ZF, где будет перехвачена, пользователю отобразится красивое окошко с текстом, “извините, тут вот ошибочка возникла, но мы уже её фиксим”, а нам на почту придёт письмо, в котором будет текст ошибки и стек. Сайт будет тестировать сами пользователи, они сами того не подозревая будут тестить наше веб-приложение, а нам остается только фиксить баги.

Это замечательно, но могут быть страницы, на которые пользователи не зайдут (т.к. например они будут слишком глубоко в иерархии сайта). Как с ними быть? Тут нам на помощь придет робот-пукт, который просто будет ходить по сайту. Скачал страничку, нашел ссылки, пошел по ссылкам, скачал странички, нашел ссылки и т.д. Есть много вариантов таких кроулеров (crawlers) как на PHP, так и на других языках. Но есть способ лучше:

Да, да, старый добрый wget может поработать у нас краулером. Он будет ходить по ссылкам, загружать странички, что нам собственно и надо.

Смотреть логи паука можно в реальном режиме с помощью команды:

Очень весело наблюдать, как он ползает по сайту. Сегодня сделали пробный запуск, а потом думаю ставить его на ночь каждый день. Пусть ползает себе, тестит 🙂

4 Comments

  1. А если БД бросает исключение типа unique constraint violated, когда, например, пользователь хочет создать аккаунт с логином, который уже занят, вы такие исключения тоже по почте рассылаете?

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

      1. Логин я привёл для примера. Бывают же и другие случаи, когда БД «отбивает» запрос, и это не ошибка, а ожидаемое поведение. Например, ограничения по внешним ключам. Все эти ограничения можно предварительно проверять и только потом выполнять запрос, но это не исключает возможности race condition, и по мне здесь гораздо надёжнее просто правильно обработать ошибку БД (которая не является ошибкой для приложения).

        1. Согласен. Как я написал в баззе, “исключения должны быть исключительные”.

Leave a Reply to Андрей Токарчук Cancel reply