img

Как протестировать PHP-код с помощью PHUnit

Существует огромное количество различных способов, как можно протестировать ваше программное приложение, и модульное тестирование – одно их самых важных.

Итак, что же такое модульное тестирование и как его можно выполнить? Это и много другое вы узнаете в этой статье.

Что такое модульное тестирование?

«Модульное тестирование – это процесс разработки программного обеспечения, в котором самые маленькие проверяемые части приложения, которые называются модулями, тестируются на работоспособность индивидуально и независимо,» - SearchSoftwareQuality.

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

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

Что такое PHPUnit?

Модульное тестирование в PHP можно выполнять с помощью PHPUnit – среды тестирования для PHP, ориентированной на программистов. PHPUnit – это представитель архитектуры xUnit для фреймворков, предназначенных для модульного тестирования. Он очень прост в установке, и с ним легко работать. 

Установка PHPUnit

PHPUnit можно установить глобально на свой сервер. Помимо этого, вы можете установить его локально для каждого проекта в качестве зависимости для вашего проекта с помощью composer. Сделать это можно прямо во время разработки. В этой статье описывается, как его можно использовать для каждого проекта в отдельности.

Для того, чтобы начать, создайте и запустите новый проект с помощью composer, используя следующие команды:

$ mkdir test-project
$ cd test-project
$ composer init

Первая команда создает папку test-project в текущем каталоге. Вторая команда перемещает в нее проект. Последняя команда запускает интерактивную оболочку.

Screenshot-2022-03-08-at-11.08.39

Подсказки по инициализации composer

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

Вы можете пропустить часть, связанную с зависимостями, поскольку мы их не устанавливаем. Предполагается, что PHPUnit – это dev-dependency, так как тестирование в целом должно производиться только во время разработки.

Теперь, когда вы увидите вопрос: Would you like to define your dev dependencies (require-dev) interactively [yes]?, нажмите Enter. После чего введите phpunit/phpunit, чтобы установить PHPUnit в качестве dev-dependency.

Подтвердите оставшиеся значения по умолчанию и переходите к созданию файла composer.json. На данный момент сгенерированный файл должен выглядеть вот так:

{
    "name": "zubair/test-project",
    "require-dev": {
        "phpunit/phpunit": "^9.5"
    },
    "autoload": {
        "psr-4": {
            "Zubair\\TestProject\\": "src/"
        }
    },
    "authors": [
        {
            "name": "Idris Aweda Zubair",
            "email": "zubairidrisaweda@gmail.com"
        }
    ],
    "require": {}
}

Сгенерированный файл composer.json

Как писать тесты в PHPUnit

Писать тесты в PHPUnit не так сложно. Ниже приведены несколько соглашений, с которых следует начать:

  • Для того, чтобы протестировать класс в PHP, вам нужно создать тестовый класс, который будет назван именем этого класса. Например, если бы у меня был некий класс User, то тестовый класс имел бы имя UserTest.
  • Тестовый класс (UserTest), как правило, наследует класс PHPUnit\Framework\TestCase.
  • Каждый тест в классе – это общедоступный метод с префиксом test. Например, для тестирования метода sayHello в классе User нам понадобиться метод под названием testSayHello.
  • Внутри тестового метода, допустим, testSayHello, вы должны использовать метод PHPUnit, например, assertSame, для того, чтобы убедиться, что какой-то метод возвращает нужное значение.

Довольно популярным соглашением считается хранение всех тестов в специальном каталоге tests, а всего исходного кода – в каталоге src.

Пример тестирования в PHPUnit

Для того, чтобы вы лучше понимали, что происходит в данной статье, ниже приведен пример класса User с простыми методами, которые мы и будем тестировать:

<?php

namespace Zubair\TestProject;

use InvalidArgumentException;

class User
{
    public int $age;
    public array $favorite_movies = [];
    public string $name;

    /**
     * @param int $age
     * @param string $name
     */
    public function __construct(int $age, string $name)
    {
        $this->age = $age;
        $this->name = $name;
    }

    public function tellName(): string
    {
        return "My name is " . $this->name . ".";
    }

    public function tellAge(): string
    {
        return "I am " . $this->age . " years old.";
    }

    public function addFavoriteMovie(string $movie): bool
    {
        $this->favorite_movies[] = $movie;

        return true;
    }

    public function removeFavoriteMovie(string $movie): bool
    {
        if (!in_array($movie, $this->favorite_movies)) throw new InvalidArgumentException("Unknown movie: " . $movie);

        unset($this->favorite_movies[array_search($movie, $this->favorite_movies)]);

        return true;
    }
}

Пример класса User

Этот класс вполне может выступать в роли класса User в вашем приложении потоковой передачи видео. У пользователя есть имя (name), возраст (age) и список любимых фильмов (favorite_movies), который можно обновлять. Что же касается остального, то дальше мы проверим, корректно ли работают все эти функции. 

Создадим класс UserTest в папке tests. Для начала добавьте в него следующее:

<?php

namespace Zubair\TestProject;

use PHPUnit\Framework\TestCase;

final class UserTest extends TestCase
{
    // Tests will go here { // Тестирования будут проводиться здесь }
}

Тестирование конструктора

В большинстве случаев метод __construct не тестируется. Но, так как мы устанавливаем в нем значения, вполне разумно было бы убедиться в том, что значения устанавливаются верно.

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

Для того, чтобы протестировать конструктор, вам нужно создать метод testClassConstructor:

public function testClassConstructor()
{
    $user = new User(18, 'John');

    $this->assertSame('John', $user->name);
    $this->assertSame(18, $user->age);
    $this->assertEmpty($user->favorite_movies);
}

Тестирование для метода _construct

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

Как запускать тесты в PHPUnit

Вы можете запускать все свои тесты с каталога с помощью двоичного файла PHPUnit, который установлен в папке поставщика.

$ ./vendor/bin/phpunit --verbose tests

Кроме того, вы можете запускать отдельные тесты, просто указав путь к файлу с тестом.

$ ./vendor/bin/phpunit --verbose tests/UserTest.php

Флаг --verbose нужен для того, чтобы получить больше информации о статусе теста.

Теперь мы можем запустит тест и увидеть результат:

Screenshot-2022-03-08-at-13.17.54

Результат теста

Мы видим, что мы выполнили 1 тест и 3 утверждения. Кроме того, мы видим, сколько времени потребовалось на запуск теста и сколько памяти было использовано при его выполнении.

Утверждение – это то, что PHPUnit использует для сравнения значений, которые возвращают методы, с ожидаемыми значениями.

В данном примере для того, чтобы проверить, соответствуют ли свойства name и age объекта user введенным значениям, используется assertSame. Кроме того, для проверки массива favorite_movies на пустоту используется assertEmpty.

Ознакомиться со всеми утверждениями вы можете в документации PHPUnit.

Изменим код и проверим, будет ли возраст пользователя соответствовать 21.

public function testClassConstructor()
{
    $user = new User(18, 'John');

    $this->assertSame('John', $user->name);
    $this->assertSame(21, $user->age);
    $this->assertEmpty($user->favorite_movies);
} 

Запустим тест еще раз, и получим вот такой результат:

Screenshot-2022-03-08-at-13.24.20

Невыполненное утверждение

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

Тестирование методов tellName и tellAge

Следующим шагом мы можем протестировать метод tellName. Этот метод определяет имя пользователя как предложение. Так что мы можем написать тест для проверки того:

  • Является ли возвращаемое значение строкой
  • Есть ли в возвращаемой строке имя пользователя (с учетом или без учета регистра)

Тест использует два утверждения: assertIsString и assertStringContainsStringIgnoringCase. Первое предназначено для проверки того, является ли возвращаемое значение строкой, а второе – содержит ли оно строку John.

Метод tellAge во многом похож на метод tellName – он использует ту же логику. Так что его тест будет аналогичен предыдущему:

public function testTellAge()
{
    $user = new User(18, 'John');

    $this->assertIsString($user->tellAge());
    $this->assertStringContainsStringIgnoringCase('18', $user->tellAge());
}

Тестирование метода addFavoriteMovie

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

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

public function testAddFavoriteMovie()
{
    $user = new User(18, 'John');

    $this->assertTrue($user->addFavoriteMovie('Avengers'));
    $this->assertContains('Avengers', $user->favorite_movies);
    $this->assertCount(1, $user->favorite_movies);
}

Здесь мы используем несколько новых утверждений: assertTrueassertContains и assertCount. Они предназначены для того, чтобы можно было проверить, истинно ли возвращаемое значение, содержит ли массив новый элемент и что в массиве стало на один элемент больше. 

Тестирование метода removeFavoriteMovie

И последнее, метод, который удаляет фильмы из списка - removeFavoriteMovie

public function testRemoveFavoriteMovie()
{
    $user = new User(18, 'John');

    $this->assertTrue($user->addFavoriteMovie('Avengers'));
    $this->assertTrue($user->addFavoriteMovie('Justice League'));

    $this->assertTrue($user->removeFavoriteMovie('Avengers'));
    $this->assertNotContains('Avengers', $user->favorite_movies);
    $this->assertCount(1, $user->favorite_movies);
}

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

Заключение

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

Ссылка
скопирована
Программирование
Скидка 25%
Python Advanced. Продвинутый курс
Освойте асинхронное и метапрограммирование, изучите аннотацию типов и напишите собственное приложение на FastAPI. Улучшите свои навыки Python, чтобы совершить быстрый рост вашего грейда до уровня middle.
Получи бесплатный
вводный урок!
Пожалуйста, укажите корректный e-mail
отправили вводный урок на твой e-mail!
Получи все материалы в telegram и ускорь обучение!
img
Еще по теме:
img
  Хотите разрабатывать игры на Python? Здесь представлен полный обзор лучших библиотек и фреймворков Python, которые вы можете и
img
Если вы хорошо знаете, что такое глубокое обучение, что, скорее всего, не раз слышали такую фразу: «PyTorch против TensorFlow».
img
  Введение Что такое стек и куча? И то, и то область памяти, но с разными механизмами распределения и управления ресурсами памят
img
  Если вы уже давно работаете с SEO, то, возможно, сталкивались с одной из концепций рендеринга - рендеринга на стороне сервера
img
Введение За счет ветвления в Git разработчики могут работать сразу над несколькими функциями или изменениями, не мешая друг друг
img
Управление памятью в операционных системах Введение Управление памятью – это критически важная и при этом довольно сложная задач
Комментарии
ОСЕННИЕ СКИДКИ
40%
50%
60%
До конца акции: 30 дней 24 : 59 : 59