General principles of QA
Общие принципы QA
Надёжность продукта начинается не в момент релиза, а задолго до него — на этапе проектирования и разработки. Чтобы системы Webdelo работали стабильно, мы закладываем принципы качества в архитектуру, процессы и культуру команды. В этом разделе — как мы внедряем тестируемость с самого начала, настраиваем автоматизацию и строим взаимодействие между разработкой и QA-инженерами на всех этапах.
Место QA в процессе разработки
Мы включаем тестирование на самых ранних этапах. Когда архитектор продумывает модули — он закладывает тестируемость. Когда разработчик пишет код — он сразу пишет и тесты. Когда QA получает задачу — он знает контекст, архитектуру и цели.
Точка входа QA — не код-ревью, а постановка задачи. Это снижает число багов и ускоряет фиксацию ошибок.
Как мы тестируем крупные проекты
Мы строим процесс так, чтобы каждая стадия логично продолжала предыдущую и проверяла важные аспекты системы.
Сначала разработчик покрывает основную бизнес-логику юнит-тестами. Эти тесты быстрые и охватывают ключевые расчёты и правила. Затем запускаются интеграционные проверки — они помогают убедиться, что сервис корректно взаимодействует с базой, очередями и внешними API.
После этого мы подключаем ручную проверку. QA-специалист проверяет интерфейсы, поведение в браузерах и сложные кейсы, где автоматизация может быть неэффективной. Далее идёт регрессия — повторное тестирование ранее работающих сценариев. На стадии pre-release убеждаемся, что обновления не ломают совместимость и система выдерживает нагрузки.
После релиза включаются инструменты мониторинга: мы следим за логами, ошибками и метриками, чтобы вовремя отреагировать, если что-то пойдёт не так.
Роль CI/CD в проверке качества
Каждое изменение в коде запускает цепочку проверок. Сначала выполняются тесты: юнит, интеграционные и e2e. Затем подключаются линтеры и статический анализ — чтобы код соответствовал стандартам команды. После этого фронтенд-сборка и регрессионные проверки сравнивают результаты с предыдущими версиями.
Если всё проходит успешно — код можно смерджить. Если нет — CI останавливается. Это исключает попадание нерабочих изменений в прод и ускоряет отладку.
Сокращение багов в продуктиве на 60 % благодаря CI/CD
после внедрения автоматического тестирования через GitLab CI, включая комплексные сценарии и регрессионные тесты, GitLab достиг значительного снижения числа багов в рабочей среде — примерно на 60 %. Команда ускорила релизы благодаря надёжной автоматизации каждого коммита.
GitLab
Типы тестов и их назначение
Мы применяем разные виды тестов, каждый из которых отвечает за свой уровень надёжности.
Юнит-тесты проверяют отдельные функции и модули. Они быстро выполняются и защищают базовую бизнес-логику. Интеграционные тесты показывают, как части системы взаимодействуют друг с другом: например, сервис и база данных. End-to-end тесты воспроизводят поведение пользователя — от входа в систему до оформления заказа.
В дополнение к этому мы проводим ручную проверку. Это помогает убедиться, что интерфейс работает корректно, всё выглядит как задумано, и система устойчива к нестандартным сценариям.
Чек-листы, тест-кейсы и сценарии
Чтобы не пропустить важные сценарии, мы оформляем проверку в понятные и воспроизводимые форматы:
- PHPUnit, Cypress, Playwright, Vitest — для автотестов на всех уровнях: от backend до интерфейса.
- Postman и Newman — для тестирования и автоматизации API.
- Sentry, Prometheus, Grafana — чтобы отслеживать ошибки, метрики, падения, производительность.
- Loki, Jaeger, Graylog — для логирования и трассировки запросов в распределённых системах.
Все инструменты встроены в CI/CD. Если тест не прошёл — мы узнаем об этом сразу.
Регресс и pre-release проверка
Перед релизом мы устраиваем полный контроль:
- Проверяем, что старые функции продолжают работать, как ожидалось (регрессия).
- Запускаем миграции на стенде и откатываем их, чтобы убедиться в надёжности.
- Сверяем метрики производительности до и после изменений.
- Проверяем, что внешние сервисы, с которыми мы интегрируемся, по-прежнему доступны и согласованы по версии.
Финальное решение — за QA. Без его подтверждения задача не идёт в релиз.
Взаимодействие QA с разработчиками
Работа QA и разработчиков строится на чётком разделении ответственности и постоянном взаимодействии.
- Кто пишет тесты? Разработчик пишет юнит-тесты для своей бизнес-логики, покрывает сервисы, запросы, контроллеры. Если компонент сложный — он также добавляет интеграционные тесты или e2e, особенно для критичных пользовательских сценариев. QA сосредоточен на пользовательских сценариях и логике, основываясь на проверках, выполненных разработчиком.
- Кто тестирует руками? Ручное тестирование — зона ответственности QA. Это визуальная проверка, UX, кросбраузерность, мобильные версии, нестандартные кейсы, интеграции. QA идёт по чек-листам, воспроизводит edge-кейсы и сравнивает поведение со спецификацией.
- Кто пишет баг-репорты? Все баги описывает QA. Он формулирует шаги воспроизведения, ожидаемый и фактический результат, окружение и скриншоты. Важно, чтобы баг-репорт был чётким и понятным без лишней интерпретации. Разработчик получает баг — и либо подтверждает, либо уточняет с автором.
- Как мы взаимодействуем?Каждый merge request проходит через QA: он проверяет покрытие, поведение, регрессию. Если нужна доработка — пишет комментарии. QA также участвует в груминге задач, чтобы заранее задать нужные сценарии. Все договорённости фиксируются в баг-репортах или тест-документации.
Такое взаимодействие помогает команде двигаться быстрее и эффективнее. Каждый знает, за что отвечает, и работает с фокусом на стабильный результат.
Code Tesing: Go
Тестирование в коде: Go
Go выбран как язык для наших высоконагруженных микросервисов. Его строгая типизация, простота и высокая производительность требуют чёткой дисциплины тестирования. Здесь особенно важно отделить основную логику приложения, убедиться, что она корректно взаимодействует с внешними сервисами, и настроить стабильную автоматическую проверку каждого изменения.
Unit-тесты в Go
Мы используем стандартный пакет testing и библиотеку testify для удобных ассертов. Юнит-тесты в Go — это быстрый и надёжный способ убедиться, что бизнес-логика работает корректно. Мы придерживаемся читаемого нейминга (TestXxx_WhenYyy_ShouldZzz) и проверяем как обычные, так и крайние случаи поведения системы — например, пустые поля, максимальные значения или неожиданные данные.
Интеграционные тесты
Когда нужно проверить взаимодействие между сервисами, базами данных или брокерами сообщений, мы применяем httptest, dockertest и тестовые конфигурации с временными контейнерами. Это позволяет запускать PostgreSQL, Redis, Kafka и другие зависимости в изолированной среде. Мы запускаем систему в условиях, максимально приближённых к реальным, но без использования настоящих пользовательских данных.
Mocking и фальш-реализации
Чтобы протестировать отдельные части системы, не затрагивая всё остальное, мы подставляем вместо настоящих компонентов специальные заглушки — они имитируют поведение, но не выполняют настоящих действий. Для этого используем инструменты gomock и mockery. Поскольку все зависимости подключаются через интерфейсы, мы легко можем заменить, например, базу данных или API на простую подделку, которая возвращает предсказуемые ответы. Это позволяет сконцентрироваться на проверке логики кода, а не на работе всей системы.
Покрытие и тестируемость
Мы проектируем модули так, чтобы их было удобно тестировать по частям. Это значит: логика вынесена отдельно, зависимости легко подменяются, а поведение можно воспроизвести в изоляции.
Чтобы понимать, насколько хорошо наш код защищён тестами, мы проверяем его «покрытие» — то есть, какие именно участки кода действительно проходят через тесты. Для этого мы используем:
- go test -coverprofile=coverage.out — создаёт специальный файл, в котором видно, какой процент строк кода был выполнен во время тестов;
- go tool cover -html=coverage.out — открывает удобную цветную визуализацию: протестированные строки подсвечены, непротестированные — нет.
Так мы сразу видим, где остались слабые места, и можем добавить нужные проверки.
Цель такого покрытия — выявить зоны риска и точки роста. Если оно снижается, мы пересматриваем архитектуру или усиливаем тесты.
CI для Go
Каждый раз, когда разработчик отправляет изменения в репозиторий, автоматически запускается серия проверок. Это делает система GitHub Actions. Она пошагово выполняет:
- Автоматический запуск всех тестов с помощью команды go test ./.... Это проверяет, не нарушена ли работа уже написанного кода.
- Проверку качества и безопасности кода: команды go vet, staticcheck и golangci-lint выявляют потенциальные ошибки, неправильные типы данных и несоблюдение кодстайла.
- Оценку покрытия тестами — чтобы понять, насколько хорошо код протестирован.
Если какая-либо проверка не проходит — процесс останавливается. Разработчик получает обратную связь и может сразу внести исправления. Это помогает избежать багов ещё до попадания изменений в основную ветку проекта. Только после успешного прохождения всех этапов код можно объединить с основной частью системы.
Code Testing: PHP / Laravel
Тестирование в коде: PHP / Laravel
В Laravel особую роль играют события, очереди, формы, миграции. Всё это должно быть покрыто тестами, иначе возрастает риск сбоев после обновлений. Каждая задача проходит юнит-проверки, сценарные тесты и ручной просмотр, если нужно. CI сразу подсказывает, где сбой, а mock-сервисы и factory-классы позволяют ускорить проверку. Благодаря такой связке Laravel остаётся предсказуемым даже в больших проектах.
Unit-тесты в Laravel
phpunit — это основной инструмент, который мы используем для автоматической проверки кода в Laravel. Он помогает нам убедиться, что ключевые части системы — например, сервисы, формы, валидация и бизнес-логика — работают как задумано. Мы пишем тесты, которые проверяют, что функция выполняет нужное действие: например, что заказ действительно создаётся, пользователь получает уведомление, а форма возвращает ошибку при неправильных данных. Мы сохраняем тесты в тех же папках, что и основной код, и придерживаемся одинаковой структуры от проекта к проекту. Это позволяет любому разработчику быстро сориентироваться: где находятся нужные тесты, за что они отвечают и как их можно запустить для проверки. Это делает процесс тестирования прозрачным и удобным в поддержке.
Feature и интеграционные тесты
Мы проверяем работу ключевых элементов приложения — маршрутов, промежуточных слоёв (middleware) и миграций базы данных. Это помогает убедиться, что пользовательские запросы проходят по нужным маршрутам, все проверки выполняются, а структура базы соответствует ожиданиям. Чтобы тесты не зависели от реальных данных и сторонних проверок, мы используем withoutMiddleware() — он отключает все промежуточные слои, и RefreshDatabase — он сбрасывает базу данных перед каждым тестом. Это даёт нам «чистое» состояние и повышает надёжность тестов. Мы проверяем не только стандартные сценарии, но и крайние случаи: пустые поля, очень длинные значения, некорректные данные — чтобы приложение устойчиво обрабатывало любые входные данные.
Mocking и фейки
Чтобы протестировать бизнес-логику без зависимости от внешних сервисов, мы подставляем вместо них временные заменители. Это помогает избежать ненадёжности, связанной с сетью, сторонними API и базами данных.
Вот как это работает:
- С помощью Mockery мы создаём «заглушки» — объекты, которые притворяются настоящими, но просто возвращают заранее заданные ответы.
- Метод Http::fake() позволяет имитировать вызовы внешних API, чтобы приложение думало, что получило ответ от реального сервера.
- Через Factory::new() мы создаём фейковые модели для тестов — например, пользователей, заказы или посты, не записывая ничего в основную базу данных.
Это делает тесты надёжными, быстрыми и воспроизводимыми. А главное — они не зависят от доступа к интернету или доступности сторонних сервисов.
Тестирование событий и очередей
Мы проверяем, что после определённых действий система запускает нужные события и ставит задачи (jobs) в очередь для последующей обработки. Это важно для всех бизнес-процессов, которые происходят в фоновом режиме — например, отправка email после регистрации. Чтобы не выполнять эти действия по-настоящему во время тестов, мы используем Event::fake() — он позволяет «перехватить» события и проверить, что они действительно были вызваны. Аналогично, с помощью Queue::assertPushed() мы убеждаемся, что задачи действительно поставлены в очередь. Это даёт нам уверенность, что приложение ведёт себя правильно и реагирует на действия пользователя так, как задумано.
CI для PHP
В Laravel-проектах мы используем GitHub Actions, чтобы автоматически проверять код каждый раз, когда кто-то вносит изменения. Это позволяет быстро обнаруживать ошибки и убедиться, что приложение работает как надо.
Вот что именно происходит:
- Сначала запускаются автоматические тесты через phpunit. Мы используем флаг --stop-on-failure, чтобы сразу остановить проверку при первой ошибке — это экономит время и показывает, где именно проблема.
- Затем включается статический анализ кода: phpstan (на максимально строгом уровне 8) и psalm помогают найти потенциальные баги и нарушения архитектурных принципов ещё до запуска приложения.
- Также проверяется файл зависимостей composer.json: команды composer validate и composer normalize гарантируют, что структура и формат этого файла корректны и согласованы.
Если хотя бы один из этих шагов не проходит, весь процесс блокируется. Это значит, что код не попадёт в основную ветку, пока ошибки не будут исправлены. Такой подход помогает команде быть уверенной: всё, что попадает в проект, уже проверено и готово к работе.
Frontend Testing: Vue.js и SPA)
Тестирование Frontend (Vue.js и SPA)
Фронтенд в Webdelo — это важная часть работы системы. Здесь обрабатываются формы, происходят обмены данными с сервером, отслеживаются состояния интерфейса и реакции на действия пользователя. Мы тщательно проверяем каждую деталь: как работают отдельные компоненты — например, кнопки или поля ввода, и как система ведёт себя в сложных ситуациях — например, когда пользователь проходит через весь путь оформления заказа. Это позволяет нам быть уверенными, что интерфейс работает стабильно, а система реагирует предсказуемо при любом сценарии.
Юнит-тесты компонентов
Мы используем инструменты Vitest или Jest, чтобы отдельно проверять работу каждого компонента интерфейса. Это значит, что мы смотрим, правильно ли отображается текст, как элемент реагирует на клики, активируются ли нужные классы и срабатывают ли условия отображения. Такие тесты не подключаются к серверу или базе — они проверяют чисто визуальное и логическое поведение компонента в отрыве от остального приложения. Это позволяет быстро выявить ошибки в отображении или взаимодействии, не тратя время на сложную настройку среды.
Интеграционные и e2e тесты
Чтобы убедиться, что пользователь может комфортно пройти через весь путь в приложении, мы используем инструменты Cypress и Playwright. С их помощью проверяются сценарии, которые происходят в реальной жизни: вход в систему, отправка формы, редактирование профиля, переход по ссылкам. Такие тесты запускают интерфейс в браузере и поэтапно проходят через действия пользователя, как если бы за компьютером сидел живой человек. Это помогает проверить, что всё работает как надо: кнопки нажимаются, страницы загружаются, данные сохраняются, ошибки не возникают
Моки API и состояния
Во время тестирования мы не обращаемся к настоящим серверам или внешним API. Вместо этого мы создаём фальшивые ответы, которые выглядят так же, как реальные, и возвращают заранее заданные данные. Это называется имитацией — мы подставляем фиктивные ответы для REST или gRPC-запросов. Такой подход помогает не зависеть от работы интернета, других сервисов и позволяет делать тесты быстрыми и стабильными. При каждом запуске тесты получают одинаковые ответы и работают в предсказуемых условиях.
Линтеры и визуальные тесты
Мы используем инструменты eslint и stylelint, чтобы автоматически проверять, нет ли в коде синтаксических ошибок или нарушений в оформлении стилей. Это помогает поддерживать код чистым и единообразным. А когда интерфейс особенно сложный или важна визуальная точность, мы добавляем скриншотные тесты — они делают снимки экранов и сравнивают их пиксель в пиксель с эталонным вариантом. Если где-то что-то сдвинулось или исчезло — тест сразу покажет это. Такой подход помогает нам не упустить визуальные баги и сохранить дизайн стабильным.
CI во фронтенде
Как только разработчик отправляет изменения в проект (пушит код), автоматически запускается целый набор проверок:
- Сначала происходит сборка проекта — чтобы убедиться, что весь код собирается без ошибок и всё работает как единое целое.
- Затем запускается линтер — он проверяет, нет ли в коде синтаксических и стилистических ошибок.
- После этого запускаются юнит-тесты — они проверяют, правильно ли работает каждая отдельная часть интерфейса.
- Если в проекте подключены визуальные тесты, система также сравнивает внешний вид страниц с эталонными снимками.
Если где-то возникает ошибка — сборка сразу прерывается, и код не попадает в основную ветку. Это позволяет команде быстро находить и устранять проблемы до того, как они попадут в продакшн.
Заключение
Тестирование в Webdelo — это часть архитектуры каждого проекта. Мы сразу закладываем возможность проверок, автоматизируем повторяющиеся действия и применяем инструменты, которые позволяют видеть качество в реальном времени.
Единый подход на всех уровнях — от backend до frontend и DevOps — помогает нам выпускать стабильные релизы, быстрее находить и устранять ошибки, развивать продукт без риска для боевой среды. Это работает благодаря дисциплине, прозрачной ответственности и общей культуре качества в команде.
Мы смотрим на результат через призму стабильности, скорости реагирования и предсказуемости работы системы в эксплуатации.