Существует огромное количество различных способов, как можно протестировать ваше программное приложение, и модульное тестирование – одно их самых важных.
Итак, что же такое модульное тестирование и как его можно выполнить? Это и много другое вы узнаете в этой статье.
Что такое модульное тестирование?
«Модульное тестирование – это процесс разработки программного обеспечения, в котором самые маленькие проверяемые части приложения, которые называются модулями, тестируются на работоспособность индивидуально и независимо,» - SearchSoftwareQuality.
Если простыми словами, то модульное тестирование подразумевает, что вы должны разбить свое приложение на самые элементарные части и протестировать их, чтобы убедиться, что каждая из них безошибочна (и безопасна).
Это тестирование автоматизировано и пишется программистами как часть их процесса разработки. Этот этап разработки очень важен, так как он помогает разработчикам создавать лучшие приложения с меньшим количеством багов.
Что такое PHPUnit?
Модульное тестирование в PHP можно выполнять с помощью PHPUnit – среды тестирования для PHP, ориентированной на программистов. PHPUnit – это представитель архитектуры xUnit для фреймворков, предназначенных для модульного тестирования. Он очень прост в установке, и с ним легко работать.
Установка PHPUnit
PHPUnit можно установить глобально на свой сервер. Помимо этого, вы можете установить его локально для каждого проекта в качестве зависимости для вашего проекта с помощью composer. Сделать это можно прямо во время разработки. В этой статье описывается, как его можно использовать для каждого проекта в отдельности.
Для того, чтобы начать, создайте и запустите новый проект с помощью composer, используя следующие команды:
$ mkdir test-project
$ cd test-project
$ composer init
Первая команда создает папку test-project в текущем каталоге. Вторая команда перемещает в нее проект. Последняя команда запускает интерактивную оболочку.
Подсказки по инициализации 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 нужен для того, чтобы получить больше информации о статусе теста.
Теперь мы можем запустит тест и увидеть результат:
Результат теста
Мы видим, что мы выполнили 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);
}
Запустим тест еще раз, и получим вот такой результат:
Невыполненное утверждение
Теперь мы видим, что мы выполнили 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);
}
Здесь мы используем несколько новых утверждений: assertTrue, assertContains и 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 в своих проектах, а также как можно протестировать свое программное обеспечение и убедиться в его качестве. Полный код для этой статьи вы можете найти здесь.