Работа начинается с тестирования

в 14:28, , рубрики: laravel, laravel 5, php, phpunit, tdd, testing, Тестирование IT-систем

В жизни каждого разработчика наступает момент, когда он задумывается над созданием тестовой составляющей для своего детища. Поправлюсь — в жизни каждого хорошего разработчика. Когда ты джуниор и не несешь особой ответственности, имеешь право на уйму ошибок и можешь их исправить в любой момент. Ты не отвечаешь за тот продукт, что создаешь и не имеешь мотивации тратить лишнюю минуту на перепроверку созданного кода. «Да ничего, этот косяк не воспроизведется», «кажется, эта штука работает», «ну как минимум, она делает то что нужно» — если вы желаете перерасти уровень программерских яслей, то придётся свести на нет каждую из этих мыслей.

С развитием собственного опыта программирования, у вас появляются новые всё более и более крутые/крупные клиенты. От некоторых вы даже будете в восторге (от всех, если вы прям везунчик) — и люди хорошие, и оплачивают щедро, и не придирчивы к возникающим проблемам. Давайте рассмотрим один такой простой случай (очень простой, но главное то, что за этим стоит) создания обработчика формы от программиста, не знающего хлопот.

Итак, поступила простая задача — написать обработчик формы. Цель — принимать заявки от клиентов на покупку кирпичей. Заказчик крупный, занимается крупными поставками кирпичей оптом (допустим, на сумму от 500 000 рублей — для того чтобы почувствовать хоть какой-то уровень ответственности за происходящее). Конкуренция бешеная — клиенты могут быстро перейти к поставщику кирпичей, если не ответить в течение суток.

Нашему программисту сказали, что нужно сохранять из формы данные клиента — ФИО представителя, номер телефона, название предприятия клиента, объем заказа и необязательное поле описания заказа. Пораскинув мозгами, быстро была создана простейшая форма со стандартными полями для лицевой части сайта:

форма

Данные с формы отправляются AJAX-запросом, без перезагрузки страницы. Далее, программист берется за оформление обработчика формы и ему нужно справиться с довольно тривиальной задачей — добавить в уже существующую таблицу Orders записи по новому клиенту и отправить заказчику письмо на почту с оповещением по новому клиенту.

простой код вставки

Форма работает, данные успешно сохраняются, заказчик доволен. Но вдруг от заказчика поступает гневный звонок “так мол и так, заказ поступил — компания-миллионер хочет купить у меня все кирпичи, но с формы не пришел номер телефона и как теперь с ними связаться?! Завтра они найдут другого поставщика! Как так, что ты сделал?! По твоей вине...”. Заказчик рвёт и мечет, минус нервы, минус доверие и минус уважение. Ситуация крайне стандартная для джуниора — отсутствие какой-либо валидации и тестирования входящих данных с формы. Первая задача (валидация) решается крайне просто, через добавление правил валидации:

валидация

Впредь, клиент сайта будет указывать только корректные данные, необходимые нам для дальнейшей обработки. На этом же этапе разработчика посещает мысль о необходимости тестирования кода для дальнейшего избежания столь неловкой ситуации. К примеру, тестирование поля фамилии будет иметь следующий вид (для упрощения базового примера, csrf защита отключена):

тест на отсутствующее поле

Мы знаем, что при отсутствии данного поля код должен вернуть ответ с ошибкой и прописанным нами статусом 400. Такие методы тестирования прописываются для каждой конкретной ситуации (или конкретной валидации поля, здесь уж всё зависит от поставленных задач и фантазии разработчика).

Но есть ли другой способ разработки, отличный от “я сделал, а теперь проверю”? Мы сначала пишем код, натыкаемся на косяки исполнения, исправляем, а потом вспоминаем про тесты. Данный подход может выйти для нас и нашего заказчика боком, учитывая потерянного многомиллионного клиента (хоть и теоретически, но всем бы таких клиентов). И тут я задался вопросом — а что если мы логику создания приложения начнем с обратного конца — сначала предъявим требования к “исполнителю”, а затем заставим его этим требованиям соответствовать? Давайте попробуем.

Задачу оставим прежнюю, поменяем лишь подход к ней. Нам нужно написать обработчик формы с полями fio, phone, corp, quant и content. Результат успешного выполнения — статус 200, добавление поля в Order с сообщением “ok” и возврат данных по внесенной записи, остальные варианты — статус 400 и список ошибок.

Первым делом нам необходимо написать метод тестирования валидного заполнения данных формы:

валидный метод

Далее, создаем необходимый роут и метод контроллера (пока пустой). Если запустим проверку сейчас, то вполне ожидаемо получим ошибку. Проверка валидного заполнения данных — это не всё, что нам нужно. Теперь приступаем к тестированию валидации полей формы. Определяем, какие поля обязательны — fio, phone, corp, quant и добавляем метод на проверку (comment не является обязательным):

пустота

Обработчик формы просто обязан будет проверить на наличие входящие данные fio, phone, corp, quant. Поскольку мы убрали все обязательные поля из запроса, то по каждому из них должна вернуться ошибка в errors. В случае, если хотя бы одного из них нет — проблема исполнения. При желании, можно добавить проверку на message, как было сделано ранее (проверка на “ok”).
Оформляем проверку на минимальную длину полей fio, phone и corp (аналогично будет сделана проверка на максимальную длину и на недопустимые символы в этих полях).

минимальная длина

Наши проверки оформлены, можно запускать и проверять

результат проверки

Идеально. Наше приложение крашнулось в 5 из 5 тестов. Дальнейшая наша цель — пройтись по тестовым методам, устанавливающим invalid значения в поля, и сформировать правила валидации на входящие данные. Логика примерно такая: поле fio не может быть пустым; длина не менее 3 и не более 120; это строка с набором символов, допускаемых в имени (буквы, дефис, отступ). Результат такой логики по всем полям:

валидация

В ответ в случае фейла добавлен список ошибок errors, которые соответствуют каждому «проблемному» полю. Это нам поможет проверять конкретные поля на валидацию (assertJsonStructure в файле теста). Далее, дописываем метод под валидную проверку и получаем итоговый вариант:

итоговый метод

И наконец-то мы можем проверить, как наш скрипт отрабатывает тестирование (напомню, было 5 фейлов в 5 тестах).

ок

Как видим, все тесты пройдены удачно и в базу внесена всего одна запись (так как только один метод был настроен на валидную работу).

результат внесён в базу

Каковы выводы? Разработка приложения, начиная с тестов, — более удачный вариант, чем обычное написание функционала. Необходимость получать от метода только то, что нужно, сравнимо с армейской дисциплиной — код делает ровно то, что ты от него требуешь, ни шага в сторону. Однако, у этого подхода есть и негативная сторона (хоть и спорная) — дело в том, что написание дополнительного функционала (которым является тестирование) также занимает часть времени, отведенного на разработку проекта. Как по мне, выбор однозначен — хороший программист должен писать тесты, и старт с тестового функционала помогает написать хорошо работающий и надежный проект. Попробую это использовать в чем-то менее тривиальном.

Автор: Виталик Иващенко

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js