Как тестировать приложения PHP с помощью PHPUnit
В этом руководстве мы вернемся к PHP и на этот раз рассмотрим, как создавать модульные тесты. Это поможет нам создавать более надежные и менее подверженные ошибкам приложения 💪.
Но прежде всего, и для тех, кто не имеет четкого представления о….
Что такое модульные тесты?
В основном это скрипты, которые мы создаем для тестирования определенных блоков кода. Тест получает данные из него и проверяет их с помощью функций, предоставляемых библиотекой, которую мы будем использовать. Таким образом, мы можем убедиться, что все хорошо, прежде чем отправить его в продакшн.
Создадим проект, чтобы посмотреть на все более наглядно.
Создаем структуру нашего проекта. Сначала мы создадим две папки в корневом каталоге, одну назовем app, а другую – tests. Далее создадим файл composer.json со следующим содержимым:
Code language: JSON / JSON with Comments (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/" > > >
После этого мы запускаем команду composer dump для создания autoload:
Далее мы установим PHPUnit. Это будет библиотека, которую мы будем использовать для создания наших тестов.
Code language: JavaScript (javascript)composer require --dev phpunit/phpunit
После этого нам нужно создать конфигурационный файл для PHPUnit. Для этого перейдем в корневой каталог нашего проекта и создадим файл phpunit.xml, который будет содержать следующий код:
Code language: HTML, XML (xml)phpunit bootstrap="vendor/autoload.php" colors="true"> testsuite name="Test directory"> directory>tests directory> testsuite> phpunit>
Фактически, мы передаем в атрибуте bootstrap файл загрузки класса, а в testsuite – каталог, в котором мы будем сохранять тесты, который в нашем случае будет находиться в папке tests.
Для этого примера мы создадим класс, который будем использовать в качестве подопытного кролика для запуска наших тестов. Для этого мы переходим в каталог приложения и внутри него создаем папку Classes. После создания этой папки мы обращаемся к ней и создаем файл Calc.php, который будет содержать следующий код:
Code language: HTML, XML (xml)namespace App\Classes; class Calc < public function sum(int $num1, int $num2) < return $num1 + $num2; > >
Как вы можете видеть, это очень простой код. В нем просто есть функция, которая берет два числа и вычисляет сумму.
После того как это сделано, пришло время создать наш первый тест. Для этого перейдем в папку tests и в ней создадим файл CalcTest.php. Очень важно, чтобы имя файла имело формат CamelCase и заканчивалось Test.php. Это способ, который использует PHPUnit для определения того, когда это тест, а когда нет. Если он не имеет такого формата, тест выполнен не будет, поэтому не забывайте об этой детали.
После создания файла мы открываем его и добавляем следующий код:
После создания файла мы открываем его и добавляем следующий код:
Code language: HTML, XML (xml)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); > >
Как вы видите, первое, что мы делаем, это добавляем библиотеку PHPUnit и наш класс Calc.
Следующим шагом будет создание класса, который должен называться так же, как и наш файл, и который будет расширять класс TestCase. Таким образом, мы сможем использовать функциональные возможности PHPUnit для создания наших тестов.
Внутри класса мы создадим наши тесты. В данном случае у нас есть только один под названием test_sum. Все методы, которые мы хотим запустить, должны начинаться с имени test_ и иметь формат названия snake_case, иначе PHPUnit проигнорирует их и они не будут запущены. Также каждый тест должен быть самодостаточным и не зависеть от других тестов, чтобы работать правильно.
Внутри test_sum мы создаем экземпляр класса Calc и выполняем метод sum, который возвращает результат. Для проверки теста мы будем использовать метод assertEquals, который является методом класса TestCase и в переводе означает что-то вроде “определить, равны ли они”. В этом методе в качестве первого параметра мы передаем результат, который ожидаем получить, а во втором – результат, который мы получили, чтобы подтвердить, что все в порядке.
Сейчас, когда мы создали наш тест, пришло время проверить его. Для этого переходим в наш корневой каталог и запускаем следующую команду:
php vendor/phpunit/phpunit/phpunit
Если все прошло успешно, мы должны получить сообщение о том, что все наши тесты успешно пройдены.
Кроме assertEquals, у нас есть еще много типов подтверждений, как вы можете видеть в этом списке. Например, если мы хотим подтвердить, что возвращаемый результат является целым числом, мы можем использовать AssertIsInt:
Code language: HTML, XML (xml)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); > >
setUp() и tearDown()
Иногда бывает так, что в различных тестах мы повторяем код и в начале, и в конце, что означает, что у нас много повторяющегося кода. Для решения этой проблемы у нас есть методы setUp и tearDown. Метод setUp всегда будет запускаться перед выполнением каждого теста, а метод tearDown будет запускаться каждый раз, когда один из наших тестов завершится.
Чтобы увидеть их в действии, вернемся к классу Calc и добавим новый метод так, чтобы теперь он выглядел следующим образом:
Code language: HTML, XML (xml)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; > >
Затем мы возвращаемся к нашему тесту и изменяем его, чтобы добавить новый тест, а также посмотреть, как применить метод setUp():
Code language: HTML, XML (xml)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); > >
Как вы видите, мы добавили метод setUp, который будет создавать экземпляр класса каждый раз, когда выполняется тест. Таким образом, наши тесты будут выглядеть чище и понятнее.
Заключение
Идея тестов заключается в том, что мы всегда запускаем их перед загрузкой в продакшн. Таким образом нам будет легче защитить наш код от ошибок, поэтому я рекомендую вам использовать их при любой возможности.
How to Test PHP Code With PHPUnit
Zubair Idris Aweda
There are many different ways to test your software application, and unit testing is an important one.
So what is unit testing and how can you do it? You’ll learn that and more in this article.
What is Unit Testing?
Unit testing is a software development process in which the smallest testable parts of an application, called units, are individually and independently scrutinised for process operation. — SearchSoftwareQuality
In basic terms, unit testing means that you break your application down to its simplest pieces and test these small pieces to ensure that each part is error free (and secure).
This testing is automated and written by software engineers as part of their development process. This is a very important step during development as it helps developers build better applications with fewer bugs.
What is PHPUnit?
You can perform unit testing in PHP with PHPUnit, a programmer-oriented testing framework for PHP. PHPUnit is an instance of the xUnit architecture for unit testing frameworks. It is very easy to install and get started with.
PHPUnit Installation
You can install PHPUnit globally on your server. You can also install it locally, on a per-project, development-time basis as a dependency to your project using composer. This article will explain how to use it on a per project basis.
To get started, create and initiate a new project with composer using these commands:
$ mkdir test-project $ cd test-project $ composer init
The first command creates a folder in your current directory, test-project and the second command moves into it. The last command starts an interactive shell.
Follow the prompt, filling in the details as required (the default values are fine). You can set the project description, author name (or contributors’ names), minimum stability for dependencies, project type, license, and define your dependencies.
You can skip the dependencies part, as we are not installing any dependencies. PHPUnit is supposed to be a dev-dependency because testing as a whole should only happen during development.
Now, when the prompt asks Would you like to define your dev dependencies (require-dev) interactively [yes]? , press enter to accept. Then type in phpunit/phpunit to install PHPUnit as a dev-dependency .
Accept the other defaults and proceed to generating the composer.json file. The generated file should look like this currently:
The output shows that we ran 1 test, and made 3 assertions in it. We also see how long it took to run the test, as well as how much memory was used in running the test.
These assertions are what PHPUnit uses to compare values returned from the methods to their expected value.
This example uses assertSame to check if the name and age properties on the user object match the entered values. It also uses assertEmpty to check that the favorite_movies array is empty.
To see a list of all these assertions, you can check out PHPUnit’s docs here.
Edit the code to check if the user age is the same as 21.
public function testClassConstructor() < $user = new User(18, 'John'); $this->assertSame('John', $user->name); $this->assertSame(21, $user->age); $this->assertEmpty($user->favorite_movies); >
Running the test again this time gives this output:
The output now shows that we ran 1 test, with 2 successful assertions, and also a failed one. We can see some explanation of the failure, showing the expected value, the gotten value, and the line where the error is from.
Test testName and tellAge
Next, we can test the testName method. This method tells the name of a user as a sentence. So, we can write the test to check:
- If the returned value is a string.
- If the returned string has the user’s name in it (with or without case sensitivity).
public function testTellName() < $user = new User(18, 'John'); $this->assertIsString($user->tellName()); $this->assertStringContainsStringIgnoringCase('John', $user->tellName()); >
The test uses the assertions assertIsString and assertStringContainsStringIgnoringCase to check that the return value is a string and that it contains the string John, respectively.
The testAge method is very similar to testName and uses the same logic. Its test will be similar to the previous one:
public function testTellAge() < $user = new User(18, 'John'); $this->assertIsString($user->tellAge()); $this->assertStringContainsStringIgnoringCase('18', $user->tellAge()); >
Test addFavoriteMovie
We can test this method, too. This method adds a movie to the list of movies. To test it, we can check if the newly added movie is in the list, and that the number of items in the list actually increased.
The latter is for confirming that items are not being displaced. Also, since the function returns some value at the end, we can check that this value is correct too.
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); >
Here, we use a few new assertions – assertTrue , assertContains , and assertCount – to check that the returned value is true, that it contains the newly added string, and that the array now has one item in it.
Test removeFavoriteMovie
Finally, we can test that the method to remove a movie works.
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); >
Here, we’re adding some movies to the list. Then, we remove one of them, and confirm that the function returned true. Next, we confirm the removal by checking that the value is no longer in the list. Finally, we confirm that we have only one movie in the list, instead of two.
Conclusion
Now you know how to set up PHPUnit in your projects and how to test and ensure that you’re building world class software. You can find all the code for this article here.
If you have any questions or relevant advice, please get in touch with me to share them.
To read more of my articles or follow my work, you can connect with me on LinkedIn, Twitter, and Github. It’s quick, it’s easy, and it’s free!