ru:https://highload.today/blogs/testiruem-veb-prilozheniya-s-pomoshhyu-laravel-i-phpunit-kak-pravilno-pisat-testy-s-primerami/ ua:https://highload.today/uk/blogs/testuyemo-vebdodatki-za-dopomogoyu-laravel-ta-phpunit-yak-pravilno-pisati-testi-z-prikladami/
logo
Тестирование      02/11/2022

Тестируем веб-приложения с помощью Laravel и PHPUnit: как правильно писать тесты — с примерами

Ігор Куницький BLOG

Senior PHP developer у Binariks

Всем привет, меня зовут Игорь, я PHP-разработчик в компании Binariks. В этой статье я расскажу вам о возможностях тестирования, которые предоставляет фреймворк Laravel в сочетании с PHPUnit, поэтому запарьте чаек и готовьтесь к лонгриду 🙂

Наличие валидных тестов с хорошим покрытием — одно из правил качественного кода. С их помощью можно быстро выявить проблемы в функционале и ускорить выход на прод. Они упрощают жизнь команды QA, уменьшая количество однотипного мануального тестирования, тем самым уменьшая влияние человеческого фактора.

В зависимости от степени изоляции тесты разделяют на следующие типы:

  • Unit Tests: максимально изолированные тесты. Используются для тестирования отдельного метода или функции. Любые внешние зависимости изолируются на уровне метода или функции.
  • Components/Service Tests: тесты, проверяющие работу и взаимодействие отдельных компонентов или сервисов. Изолируются в пределах тестируемого функционала.
  • API/HTTP Tests: тесты, проверяющие, как работают отдельные АРI и работу всех вызываемых при выполнении запроса компонентов.
  • Gui Tests: проверяют работу, фронтенд- и бэкенд-части в комплексе.

Как нужно проводить тесты

Перед тем, как начать рассказ о возможностях фреймворка, думаю, стоит напомнить правила хороших тестов:

  • Тесты должны быть быстрыми.
  • Следует избегать избыточной абстракции в тестах. Код должен быть читаемым и понятным без излишнего копания.
  • Онлайн-курс "Режисура та візуальний сторітелінг" від Skvot.
    Перетворюй свої ідеї на сильні історії в рекламі, кліпах чи кіно Досвідом ділиться режисер, продюсер та власник продакшену, який 10+ років у професії.
    Детальніше про курс
  • Названия тестов должны быть читабельными.
  • Test-Driven Development (TDD) — это методология, когда тесты пишутся перед имплементированием определенного функционала. Преимущества этого подхода в том, что вы сразу будете писать будущий код так, чтобы его можно было легко тестировать. Эта методология также уменьшает количество регрессивных тестов (тесты, покрывающие функционал после его имплементации).
  • Подготовь, проведи, проверь.

У хорошего теста три стадии:

  • подготовка  — генерирование входящих данных и состояний системы;
  • проведение теста  — проведение действий, необходимых для теста;
  • проверка — проведение действий по проверке исходных данных и состояний системы.

  • Тест должен быть воспроизводимым и возвращать одинаковый результат вне зависимости от количества вызовов.
  • Англійська для IT від Englishdom.
    В межах курсу можна освоїти ключові ІТ-теми та почати без проблем говорити з іноземними колегами.
    Дійзнайтеся більше
  • Тесты не должны зависеть друг от друга. Если у одного теста будет зависимость от другого, он может вернуть ошибку при отдельном вызове.
  • Тест должен проверять только один конкретный тестовый сценарий.
  • Входящие данные тестов должны быть реалистичными (использовать фейкеры для генерирования тестовых данных — хорошая практика).
  • Если для тестирования нужно использовать базу данных, создайте отдельную, на которой вы будете проводить тесты.
  • Избегайте логических выражений в тестах. На рисунке ниже покажу примеры того, как ДЕЛАТЬ НЕ СТОИТ:

  • Все внешние зависимости в тесте должны быть изолированы.
  • Всегда проверяйте данные соответствующими методами. Проверка данных должна производиться не только по значению, но и по типу.
  • Для максимальной пользы тестов интегрируйте ваши тесты в CI/CD — это поможет вам избежать человеческого фактора, который всегда присутствует в разработке.
  • Курс QA від Mate academy.
    Найпростіший шлях розпочати кар'єру в ІТ та ще й з гарантованим працевлаштуванням.
    Інформація про курс
  • Покрытие тестами должно быть максимально возможным. Пишите тесты для разных сценариев. Идеально, когда все возможные варианты работы функционала покрыты тестами. Хорошим показателем считается, когда покрыто хотя бы 70-80% функционала.
  • Помимо всего перечисленного выше, тесты могут служить примерами того, как работает тестируемый функционал. Поэтому хорошо рассматривать тесты как часть спецификации или документации.

Особенности тестирования в Laravel с помощью PHPUnit

Поддержка тестирования с помощью PHPUnit включена «из коробки», а файл phpunit.xml уже сконфигурирован для вашей программы. Также во фреймворк добавлено много вспомогательных методов, которые удобны и упрощают тестирование.

По умолчанию каталог tests в Laravel содержит две папки Feature и Unit.

Unit

Юнит-тесты предназначены для тестирования небольшой изолированной части вашего кода. Отдельный метод класса или функции.

Тесты в директории Unit не инициируют ваше Laravel-приложение, поэтому с юнит-тестами вы не сможете получить доступ к сервисам Laravel или базе данных.

Приведу пример написания юнит-тестов с практическим применением вышеперечисленных правил.

Задание: имплементировать метод getSubscription в классе SubscriptionService, который будет предлагать пользователю подписки в зависимости от типа его аккаунта:

  • если у пользователя есть премиум-аккаунт — предлагать ему премиум-подписки;
  • Онлайн-курс "Маркетолог" від Laba.
    Пройдіть повний шлях розробки маркетингових стратегій на практиці та з фідбеком від CEO бренд-маркетингової агенції.
    Програма курсу і реєстрація
  • если у пользователя обычный аккаунт — предлагать ему базовые подписки.

Согласно методологии TDD, начнем с написания тестов и описываем ожидаемое поведение метода:

Создаем сам функционал:

Вызываем тесты:

Юнит-тесты полезны для проверки работы отдельных важных элементов кода. Они быстры и относительно просто пишутся, дают высокую стабильность коду, покрытому тестами.

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

Курс-професія "Дизайнер інтер'єрів" від Skvot.
Велика практична програма для всіх, хто хоче засвоїти професію дизайнера інтер'єрів і заробляти на реальних проєктах відразу після курсу. Досвідом та інсайтами діляться одразу три лектори.
Програма курсу

Feature

В отличие от директории Unit, тесты в каталоге Feature предназначены для тестирования взаимодействия разных компонентов программы. Они инициируют ваше Laravel-приложение.

Соответственно, с помощью этих тестов можно проверять большую часть вашего кода, начиная от отдельных методов, работающих в инфраструктуре Laravel, до того, как несколько объектов взаимодействуют друг с другом или даже полный запрос HTTP, включая ответ с сервера.

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

Написание Feature-тестов имеет несколько особенностей:

  • Поскольку тесты имеют доступ к базе данных, для тестирования следует создать отдельную БД, где будут генерироваться и тестироваться данные.
  • Файл PHPunit.xml позволит задать или перезаписать все .envпеременные (в том числе и соответствующие конфигурации). Кроме того, вы можете создать файл .env.testing в таком случае он будет использоваться вместо .env-файла при тестировании.
  • Для обновления данных Laravel предлагает трейт Illuminate\ Foundation\ Testing\ RefreshDatabase, совершающий все миграции и инициирующий транзакцию, которая вернет вашу базу данных в исходное состояние после завершения теста.
  • Для генерирования тестовых данных следует использовать Faker, Factories, Seeders.
  • При тестировании HTTP-запросов нужно использовать функционал Named Routes — это простой и удобный способ генерирования сложных URL.
  • Курс English For IT: Communication від Enlgish4IT.
    Почни легко працювати та спілкуватися з мультикультурними командами та міжнародними клієнтами. Отримайте знижку 10% за промокодом ITCENG.
    Інформація про курс
  • Используйте Laravel Mocks для логинов, загрузки файлов, Events и т.д.

Тестирование компонента. Пример: в модель User нужно добавить scope, который будет фильтровать записи в базе данных по полю is_admin:

  • если у scope будет передаваться true — возвращать все записи, в которых поле is_admin true;
  • если scope будет передаваться false — возвращать все записи, в которых поле is_admin false.

Напишем тест:

Добавим соответствующий scope в модель User:

Убедимся, что тест работает корректно:

Онлайн-курс "Директор з продажу" від Laba.
Як стратегічно впливати на дохід компанії, мотивувати сейлзів перевиконувати KPI та впроваджувати аналітику — навчить комерційний директор Laba з 12-річним досвідом у продажах.
Приєднатись до курсу

Добавим тест для второго случая:

Проверим оба случая:

Тестирование АРI. Пример:

  • написать тест, который будет проверять следующий функционал;
  • создать endpoint POST: api/articles;
  • этот endpoint должен хранить в базе данных ( user_id (id залогиненного пользователя), title, text);
  • Онлайн-курс "2D Animation" від Skvot.
    Покроково та з фідбеком від лекторки увійдіть у 2D-анімацію через вивчення софтів, інструментів та створення кейсу у портфоліо.
    Програма курсу та реєстрація
  • доступ к endpoint может иметь только пользователь с правами admin( users.is_admin === true);
  • при успешном выполнении ответ с сервера статус 200 и последующие поля.
{
    “user_id”: (id користувача),
    “id”: (article id),
    “title”: (поле title)
}

 
<?php
​
namespace Tests\Feature;
​
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Tests\TestCase;
​
class CreateArticleTest extends TestCase
{
    use RefreshDatabase;
    use WithFaker;
​
    public function setUp(): void
    {
        parent::setUp();
​
        // исключаем из теста все мидлверы, не относящиеся к тесту
        $this->withoutMiddleware();
    }
​
    public function testCreateArticleSuccess()
    {
        // Создаем пользователя-админа
        $user = User::factory()->create([
            'is_admin' => true,
        ]);
​
        // С помощью фейкера генерируем данные запроса
        $request = [
            'title' => $this->faker()->text(6),
            'text' => $this->faker()->text(50),
        ];
​
        // С помощью метода actingAs() имитируем поведение запроса для админа
        $response = $this->actingAs($user)
            // исполняем сам запрос
            ->post(
                // первый параметр – роут, сгенерированный с помощью Laravel функционала Named routes
                route('articles.create'),
                // Второй параметр - request body
                $request,
            );
​
        // Проверка результата:
        // Проверяем, есть ли ответ с сервера 200
        $response->assertStatus(200)
            // Проверка структуры ответа
            ->assertJsonStructure([
                'id',
                'user_id',
                'title',
            ])
            // проверка достоверности полученных данных
            ->assertJson([
                'user_id' => $user->id,
                'title' => $request['title'],
            ])
            // Конвертируем ответ в array, чтобы получить id статьи
            ->json();
​
        // Проверяем, была ли создана запись в базе данных
        $this->assertDatabaseHas('articles', [
            'id' => $response['id'],
            'user_id' => $response['user_id'],
            'title' => $response['title'],
            'text' => $request['text'],
        ]);
    }
}

 

У Laravel «из коробки» есть много методов, которые будут полезны при тестировании. Кратко пройдусь по ним и добавлю полезные ссылки (на документацию 🙂 ):

1) HTTP-тесты. Название говорит само за себя — их цель — провести тестирование конкретных эндпойнтов сервера. Часто при тестировании приходится сталкиваться со следующими методами фреймворка: 

  • withoutMiddleware() помогает отключить все или заданные Middleware;
  • withHeaders(), withSession(), withCookies() — имитируют наличие в запросе определенных хедеров, сессий, куки;
  • actingAs() имитирует поведение сервера при определенном авторизованном пользователе;
  • get(), post(), put(), patch(), delete(), json() — имитирует соответствующие методы вызова севера;
  • Онлайн-курс Бізнес-аналіз. Basic Level від Ithillel.
    В ході курсу студенти навчаться техніці збору і аналізу вимог, документуванню та управлінню документацією, управлінню ризиками та змінами, а також навчаться моделювати процеси і прототипуванню.
    Приєднатися
  • assertStatus(), assertJson(), assertJsonStructure() — предоставляют возможность проверки статуса ответа с сервера и его структуры;
  • view(), blade() возможность проверки сгенерированных фреймворком страниц.

2) Методы для имитации работы функционала:

  • во фреймворке предусмотрена возможность имитирования очередей, загрузок файлов, нотификаций, передачи в контейнер имитации работы определенного объекта, работы со временем.

3) Тестирование базы данных:

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

4) Методы для тестирования консоли:

  • фреймворк предоставляет возможности тестирования входных и выходных данных артисановской консоли;
  • с установкой пекеджа Dusk появляется возможность имитирования работы браузеров и написания GUI-тестов.
  • Онлайн-курс "Продуктова аналітика" від Laba.
    Станьте універсальним аналітиком, опанувавши 20+ інструментів для роботи з будь-яким продуктом.
    Дізнатись більше про курс

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

Надеюсь, эта статья будет вам полезна. Всем удачи!

Читайте также: Как написать одностраничное приложение на Laravel и Vue.js за 45 минут

If you have found a spelling error, please, notify us by selecting that text and pressing Ctrl+Enter.

Онлайн-курс "PR Basis" від Skvot.
Дізнайся нюанси різних сфер і обрери свою.Як результат — матимеш стратегію бренду у своєму портфоліо та зможеш стартувати в піарі. Інсайтами ділиться лекторка, яка має 9+ років досвіду.
Детальніше про курс

Этот материал – не редакционный, это – личное мнение его автора. Редакция может не разделять это мнение.

Топ-5 самых популярных блогеров марта

PHP Developer в ScrumLaunch
Всего просмотровВсего просмотров
2434
#1
Всего просмотровВсего просмотров
2434
Founder at Shallwe, Python Software Engineer (Django/React)
Всего просмотровВсего просмотров
113
#2
Всего просмотровВсего просмотров
113
Career Consultant в GoIT
Всего просмотровВсего просмотров
95
#3
Всего просмотровВсего просмотров
95
CEO & Founder в Trustee
Всего просмотровВсего просмотров
94
#4
Всего просмотровВсего просмотров
94
Рейтинг блогеров

Ваша жалоба отправлена модератору

Сообщить об опечатке

Текст, который будет отправлен нашим редакторам: