В этом руководстве мы вернемся к PHP и на этот раз рассмотрим, как создавать модульные тесты. Это поможет нам создавать более надежные и менее подверженные ошибкам приложения 💪.
Но прежде всего, и для тех, кто не имеет четкого представления о….
Что такое модульные тесты?
В основном это скрипты, которые мы создаем для тестирования определенных блоков кода. Тест получает данные из него и проверяет их с помощью функций, предоставляемых библиотекой, которую мы будем использовать. Таким образом, мы можем убедиться, что все хорошо, прежде чем отправить его в продакшн.
Создадим проект, чтобы посмотреть на все более наглядно.
Создаем структуру нашего проекта. Сначала мы создадим две папки в корневом каталоге, одну назовем app, а другую – tests. Далее создадим файл composer.json со следующим содержимым:
{
"name": "alber/unit-test-php",
"description": "Руководство по настройке проекта с помощью PHPUnit",
"type": "project",
"license": "MIT",
"authors": [
{
"name": "Ivan",
"email": "ivan@gmail.com"
}
],
"minimum-stability": "stable",
"require": {
},
"autoload": {
"psr-4": {
"App\\": "app/"
}
}
}
Code language: JSON / JSON with Comments (json)
После этого мы запускаем команду composer dump для создания autoload:
composer dump
Далее мы установим PHPUnit. Это будет библиотека, которую мы будем использовать для создания наших тестов.
composer require --dev phpunit/phpunit
Code language: JavaScript (javascript)
После этого нам нужно создать конфигурационный файл для PHPUnit. Для этого перейдем в корневой каталог нашего проекта и создадим файл phpunit.xml, который будет содержать следующий код:
<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="vendor/autoload.php" colors="true">
<testsuite name="Test directory">
<directory>tests</directory>
</testsuite>
</phpunit>
Code language: HTML, XML (xml)
Фактически, мы передаем в атрибуте bootstrap файл загрузки класса, а в testsuite – каталог, в котором мы будем сохранять тесты, который в нашем случае будет находиться в папке tests.
Для этого примера мы создадим класс, который будем использовать в качестве подопытного кролика для запуска наших тестов. Для этого мы переходим в каталог приложения и внутри него создаем папку Classes. После создания этой папки мы обращаемся к ней и создаем файл Calc.php, который будет содержать следующий код:
<?php
namespace App\Classes;
class Calc
{
public function sum(int $num1, int $num2)
{
return $num1 + $num2;
}
}
Code language: HTML, XML (xml)
Как вы можете видеть, это очень простой код. В нем просто есть функция, которая берет два числа и вычисляет сумму.
После того как это сделано, пришло время создать наш первый тест. Для этого перейдем в папку tests и в ней создадим файл CalcTest.php. Очень важно, чтобы имя файла имело формат CamelCase и заканчивалось Test.php. Это способ, который использует PHPUnit для определения того, когда это тест, а когда нет. Если он не имеет такого формата, тест выполнен не будет, поэтому не забывайте об этой детали.
После создания файла мы открываем его и добавляем следующий код:
После создания файла мы открываем его и добавляем следующий код:
<?php
use PHPUnit\Framework\TestCase;
use App\Classes\Calc;
class CalcTest extends TestCase
{
public function test_sum()
{
$calc = new Calc();
$result = $calc->sum(1, 2);
$this->assertEquals(3, $result);
}
}
Code language: HTML, XML (xml)
Как вы видите, первое, что мы делаем, это добавляем библиотеку PHPUnit и наш класс Calc.
Следующим шагом будет создание класса, который должен называться так же, как и наш файл, и который будет расширять класс TestCase. Таким образом, мы сможем использовать функциональные возможности PHPUnit для создания наших тестов.
Внутри класса мы создадим наши тесты. В данном случае у нас есть только один под названием test_sum. Все методы, которые мы хотим запустить, должны начинаться с имени test_ и иметь формат названия snake_case, иначе PHPUnit проигнорирует их и они не будут запущены. Также каждый тест должен быть самодостаточным и не зависеть от других тестов, чтобы работать правильно.
Внутри test_sum мы создаем экземпляр класса Calc и выполняем метод sum, который возвращает результат. Для проверки теста мы будем использовать метод assertEquals, который является методом класса TestCase и в переводе означает что-то вроде “определить, равны ли они”. В этом методе в качестве первого параметра мы передаем результат, который ожидаем получить, а во втором – результат, который мы получили, чтобы подтвердить, что все в порядке.
Сейчас, когда мы создали наш тест, пришло время проверить его. Для этого переходим в наш корневой каталог и запускаем следующую команду:
php vendor/phpunit/phpunit/phpunit
Если все прошло успешно, мы должны получить сообщение о том, что все наши тесты успешно пройдены.
Кроме assertEquals, у нас есть еще много типов подтверждений, как вы можете видеть в этом списке. Например, если мы хотим подтвердить, что возвращаемый результат является целым числом, мы можем использовать AssertIsInt:
<?php
use PHPUnit\Framework\TestCase;
use App\Classes\Calc;
class CalcTest extends TestCase
{
public function test_sum()
{
$calc = new Calc();
$result = $calc->sum(1, 2);
$this->assertEquals(3, $result);
$this->assertIsInt($result);
}
}
Code language: HTML, XML (xml)
setUp() и tearDown()
Иногда бывает так, что в различных тестах мы повторяем код и в начале, и в конце, что означает, что у нас много повторяющегося кода. Для решения этой проблемы у нас есть методы setUp и tearDown. Метод setUp всегда будет запускаться перед выполнением каждого теста, а метод tearDown будет запускаться каждый раз, когда один из наших тестов завершится.
Чтобы увидеть их в действии, вернемся к классу Calc и добавим новый метод так, чтобы теперь он выглядел следующим образом:
<?php
namespace App\Classes;
class Calc
{
public function sum(int $num1, int $num2)
{
return $num1 + $num2;
}
public function res(int $num1, int $num2)
{
return $num1 - $num2;
}
}
Code language: HTML, XML (xml)
Затем мы возвращаемся к нашему тесту и изменяем его, чтобы добавить новый тест, а также посмотреть, как применить метод setUp():
<?php
use PHPUnit\Framework\TestCase;
use App\Classes\Calc;
class CalcTest extends TestCase
{
public $calc = null;
public function setUp(): void
{
$this->calc = new Calc();
}
public function test_sum()
{
$result = $this->calc->sum(1, 2);
$this->assertEquals(3, $result);
$this->assertIsInt($result);
}
public function test_res()
{
$result = $this->calc->res(2, 1);
$this->assertEquals(1, $result);
$this->assertIsInt($result);
}
}
Code language: HTML, XML (xml)
Как вы видите, мы добавили метод setUp, который будет создавать экземпляр класса каждый раз, когда выполняется тест. Таким образом, наши тесты будут выглядеть чище и понятнее.
Заключение
Идея тестов заключается в том, что мы всегда запускаем их перед загрузкой в продакшн. Таким образом нам будет легче защитить наш код от ошибок, поэтому я рекомендую вам использовать их при любой возможности.