Зарегистрируйтесь для доступа к 15+ бесплатным курсам по программированию с тренажером

Модульные тесты PHP: Автоматическое тестирование

Изученной информации уже достаточно для тестирования в повседневной практике разработки. Перед тем, как погружаться в более сложные темы и возможности PHPUnit, пройдём полный путь тестирования библиотеки, поговорим об организации тестов, хороших и плохих практиках. Это поможет сформировать правильное отношение к тестированию в целом.

В этом уроке мы разберём основы модульного тестирования. Это тестирование направлено на проверку модулей программы в изоляции от всех остальных частей. Эти тесты обычно тестируют базовые конструкции языка: функции, модули, классы. Такие тесты не дают никаких гарантий работы всего приложения в целом, но хорошо помогают тогда, когда какой-то модуль программы имеет сложную логику.

Попробуем протестировать стек. Напомню, что стек представляет собой список элементов организованных по принципу LIFO. Данные кладутся в стек в одном порядке, а извлекаются в обратном. Сам стек, как правило, используется для реализации алгоритмов. Он часто используется в низкоуровневом коде: например, внутри языков программирования или в операционных системах.

<?php

// Вымышленная библиотека
use Stack;

$stack = Stack\make();
Stack\isEmpty($stack); // true
Stack\push($stack, 1); // (1)
Stack\push($stack, 2); // (2, 1)
Stack\push($stack, 3); // (3, 2, 1)
Stack\isEmpty($stack); // false
Stack\pop($stack); // 3. В стеке (2, 1)
Stack\pop($stack); // 2. В стеке (1)
Stack\pop($stack); // 1. В стеке пусто
Stack\isEmpty($stack); // true

Сначала решим организационные вопросы. Если предположить, что реализация стека лежит в файле src/Stack.php, то его тест мы положим в файл tests/StackTest.php.

Тестируем основную функциональность

Теперь напишем первый тест. Первый тест всегда должен проверять позитивный сценарий — тот, в котором задействована основная функциональность тестируемого компонента:

<?php

namespace Hexlet\Phpunit\Tests;

use PHPUnit\Framework\TestCase;
use Hexlet\Phpunit\Stack;

class StackTest extends TestCase
{
    public function testMainFlow(): void
    {
        $stack = Stack\make();
        // Добавляем два элемента в стек и затем извлекаем их
        Stack\push($stack, 'one');
        Stack\push($stack, 'two');

        $value1 = Stack\pop($stack);
        $this->assertEquals('two', $value1);
        $value2 = Stack\pop($stack);
        $this->assertEquals('one', $value2);
    }
}

Этот тест проверяет, что правильно работают два основных метода без учёта пограничных случаев. Для этого внутри теста проверяются два утверждения, которые по очереди проверяют извлекаемые значения из стека.

Тестируем дополнительную функциональность

Следующим тестом будет тест на дополнительные функции стека. К таким у нас относится функция isEmpty(), которая проверяет, пустой ли стек:

<?php

namespace Hexlet\Phpunit\Tests

use PHPUnit\Framework\TestCase;
use Hexlet\Phpunit\Stack;

class StackTest extends TestCase
{
    public function testIsEmpty(): void
    {
        $stack = Stack\make();
        $this->assertTrue(Stack\isEmpty($stack));
        Stack\push($stack, 'one');
        $this->assertFalse(Stack\isEmpty($stack));
        Stack\pop($stack);
        $this->assertTrue(Stack\isEmpty($stack));
    }
}

В этом тесте проверяются сразу три ситуации:

  • начальное состояние стека
  • состояние стека после добавления элементов
  • состояние стека после извлечения всех элементов

В принципе, этого достаточно. Хотя в теории возможны ситуации, при которых isEmpty() всё равно сломается. Нужно ли пытаться найти все варианты? Не нужно. Тесты не даются бесплатно, каждая написанная строчка кода в проекте — потенциальное место для изменения в случае правок. Если есть сомнения, нужно ли писать проверку или нет, то лучше не пишите. Так вы поймёте тот минимум, который стоит писать, и после которого тесты писать не эффективно. Редкие ситуации требуют покрытия тестами только тогда, когда они критичны для работоспособности.

Пограничные случаи

Ну, и последнее, что можно протестировать — поведение функции pop(), когда в стеке нет ни одного элемента. По задумке, стек выбрасывает исключение, если из него попытались взять элемент, когда тот был пустой. То есть эта ситуация рассматривается как ошибочная, программист всегда должен убеждаться в том что стек не пустой. Для отлова исключений в PHPUnit используется специальное утверждение expectException()

<?php

namespace Hexlet\Phpunit\Tests

use PHPUnit\Framework\TestCase;
use Hexlet\Phpunit\Stack;

class StackTest extends TestCase
{
    public function testPop(): void
    {
        // Ожидание ставится до вызова кода
        $this->expectException(Exception::class);

        $stack = Stack\make();
        Stack\pop($stack); // Boom!
    }
}

https://replit.com/@hexlet/php-testing-unit-tests-stack#tests/StackTest.php

Но не всегда пограничные случаи так легко увидеть. Маловероятно, что любой программист сможет сразу написать все нужные тесты. Если в коде возникла ошибка, для которой не было теста, то сначала напишите тест, который воспроизводит эту ошибку, и затем уже чините её. Только так можно поддерживать достаточный уровень надёжности, не превращая разработку в непрерывную починку багов.


Дополнительные материалы

  1. Чек-лист хороших инженерных практик в компаниях

Аватары экспертов Хекслета

Остались вопросы? Задайте их в разделе «Обсуждение»

Вам ответят команда поддержки Хекслета или другие студенты.

Ошибки, сложный материал, вопросы >
Нашли опечатку или неточность?

Выделите текст, нажмите ctrl + enter и отправьте его нам. В течение нескольких дней мы исправим ошибку или улучшим формулировку.

Что-то не получается или материал кажется сложным?

Загляните в раздел «Обсуждение»:

  • задайте вопрос. Вы быстрее справитесь с трудностями и прокачаете навык постановки правильных вопросов, что пригодится и в учёбе, и в работе программистом;
  • расскажите о своих впечатлениях. Если курс слишком сложный, подробный отзыв поможет нам сделать его лучше;
  • изучите вопросы других учеников и ответы на них. Это база знаний, которой можно и нужно пользоваться.
Об обучении на Хекслете

Для полного доступа к курсу нужен базовый план

Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.

Получить доступ
900
упражнений
2000+
часов теории
3200
тестов

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно.

  • 120 курсов, 2000+ часов теории
  • 900 практических заданий в браузере
  • 360 000 студентов
Отправляя форму, вы соглашаетесь c «Политикой конфиденциальности» и «Условиями оказания услуг»

Наши выпускники работают в компаниях:

Логотип компании Альфа Банк
Логотип компании Aviasales
Логотип компании Yandex
Логотип компании Tinkoff
Рекомендуемые программы

С нуля до разработчика. Возвращаем деньги, если не удалось найти работу.

Иконка программы PHP-разработчик
Профессия
Разработка веб-приложений на Laravel
20 октября 8 месяцев

Есть вопрос или хотите участвовать в обсуждении?

Зарегистрируйтесь или войдите в свой аккаунт

Отправляя форму, вы соглашаетесь c «Политикой конфиденциальности» и «Условиями оказания услуг»