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

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

Я стану лучше — в жизни каждого хорошего разработчика.

Когда ты юниор и на тебе не так много ответственности, ты имеешь право на массу ошибок и можешь их исправить в любой момент. Вы не несете ответственности за продукт, который создаете, и у вас нет мотивации тратить лишнюю минуту на перепроверку созданного кода.

«Нет проблем, эта ошибка не воспроизводится», «эта штука вроде работает», «ну, по крайней мере, она делает то, что нужно» — если вы хотите перерасти уровень детской программирования, вам придется свести на нет каждую из этих мыслей.

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

Некоторым из них вы даже обрадуетесь (всем, если очень повезет) - люди хорошие, платят щедро, к возникающим проблемам не придирчивы.

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

Итак, пришла простая задача — написать обработчик формы.

Цель — принимать заявки от клиентов на покупку кирпичей.

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

Конкуренция жесткая — клиенты могут быстро переключиться на поставщика кирпича, если не ответят в течение 24 часов.

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

Поразмыслив, быстро была создана простая форма со стандартными полями для лицевой части сайта:

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

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

Далее программист берется за проектирование обработчика формы и ему необходимо справиться с достаточно тривиальной задачей — добавить записи о новом клиенте в существующую таблицу «Заказы» и отправить клиенту уведомление по электронной почте о новом клиенте.



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

Форма работает, данные успешно сохраняются, клиент доволен.

Но вдруг раздается гневный звонок от заказчика: «Так мол, так, заказ получен — компания-миллионер хочет у меня выкупить все кирпичи, но из формы не пришел номер телефона, и как мне связатьсяЭ» их сейчас?! Завтра они найдут другого поставщика! Почему, что ты сделал?! Это ты виноват.» Заказчик рвется и бежит, минус нервы, минус доверие и минус уважение.

Ситуация крайне стандартная для джуниора — отсутствие какой-либо валидации и тестирования входящих данных из формы.

Первая задача (проверка) решается предельно просто добавлением правил валидации:

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

Отныне клиент сайта будет предоставлять только правильные данные, необходимые нам для дальнейшей обработки.

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

Например, проверка поля фамилии будет выглядеть так (для упрощения базового примера отключена защита csrf):

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

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

А есть ли другой путь развития, кроме «сделал, сейчас проверю»? Мы сначала пишем код, натыкаемся на проблемы с исполнением, исправляем их, а потом вспоминаем о тестах.

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

И тут я задумался — а что, если начать логику создания приложения с противоположного конца — сначала предъявить требования «исполнителю», а потом заставить его эти требования выполнить? Давай попробуем.

Оставим проблему прежней, просто изменим подход к ней.

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

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

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

Далее создаем необходимый маршрут и метод контроллера (пока пусто).

Если мы запустим проверку сейчас, то вполне ожидаемо получим ошибку.

Проверка достоверности данных — это не все, что нам нужно.

Теперь приступим к тестированию проверки полей формы.

Определяем, какие поля обязательны для заполнения — fio, phone, corp, quant и добавляем метод для проверки (комментарий необязателен):

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

Обработчику формы останется просто проверить наличие входящих данных fio, phone, corp, quant. Поскольку мы удалили из запроса все обязательные поля, то для каждого из них в error должна быть возвращена ошибка.

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

При желании можно добавить галочку на сообщение, как это было сделано ранее (галочка на «ок»).

Проверяем минимальную длину полей fio, phone и corp (аналогично будет произведена проверка максимальной длины и недопустимых символов в этих полях).



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

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



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

Идеальный.

Наше приложение вылетело в 5 из 5 тестов.

Наша дальнейшая цель — пройти тестовые методы, которые устанавливают недопустимые значения в полях и создать правила валидации входящих данных.

Логика примерно такая: поле fio не может быть пустым; длина не менее 3 и не более 120; это строка с набором символов, разрешенным в имени (буквы, дефис, отступ).

Результат этой логики для всех полей:

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

В ответ на сбой добавлен список ошибок, соответствующий каждому полю «проблема».

Это поможет нам проверить определенные поля на предмет проверки (assertJsonStructure в тестовом файле).

Далее добавляем метод валидной проверки и получаем окончательную версию:

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

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



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

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



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

Каковы выводы? Разработка приложения, начинающаяся с тестов, — лучший вариант, чем просто написание функциональности.

Необходимость получить от метода только то, что вам нужно, сравнима с военной дисциплиной — код делает именно то, что вы от него просите, не делая ни шагу в сторону.

Однако у этого подхода есть и отрицательная сторона (хотя и спорная) — дело в том, что написание дополнительного функционала (а именно тестирование) тоже отнимает часть времени, отведенного на разработку проекта.

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

Теги: #Тестирование ИТ-систем #php #тестирование #Laravel #phpunit #tdd #laravel 5

Вместе с данным постом часто просматривают: