Статьи

Обзор PHPUnit 5 — что нового? Что грядет?

Этот пост был рецензирован Клаудио Рибейро и Томасом Пунтом . Большое спасибо рецензентам, которые постоянно поддерживают контент SitePoint!


В январе 2016 года команда разработчиков PHPUnit объявила о выпуске PHPUnit 5.0.

Цветное перо на бумаге, в некоторых клетках ставятся галочки, а в других - красные крестики.

С тех пор несколько минорных версий уже были выпущены, основная версия PHPUnit 5 представила несколько новых функций и устарела несколько других . В этой статье мы рассмотрим наиболее заметные изменения.

Требования

PHPUnit 5 требует PHP 5.6. Они отказались от поддержки PHP 5.3, 5.4 и 5.5, так как они не активно поддерживаются проектом PHP, согласно их процессу выпуска . Вы можете увидеть полный список необходимых расширений PHP здесь .

Новые методы утверждения

В стек были добавлены три метода утверждения, в том числе assertFinite() , assertInfinite() и assertNan() .

assertFinite() утверждает, что ожидаемое значение является конечным значением, что означает, что это допустимое число в допустимом диапазоне для числа с плавающей точкой PHP на платформе, на которой выполняется тест. За кулисами он использует встроенную функцию PHP is_finite , чтобы проверить, соблюдено ли ограничение.

Рассмотрим следующий тест:

 <?php class FiniteTest extends PHPUnit_Framework_Testcase { public function testFinite() { $this->assertFinite(INF); } } 

Вышеприведенный тест потерпит неудачу, поскольку INF — это константа PHP, представляющая бесконечное значение.

assertInfinite() проверяет, является ли фактическое значение бесконечным числом (положительным или отрицательным), что означает слишком большое значение, чтобы поместиться в число с плавающей точкой на текущей платформе. Этот метод использует PHP-функцию is_infinite .

 <?php class InfiniteTest extends PHPUnit_Framework_Testcase { public function testInfinite() { $this->assertInfinite(INF); } } 

Приведенное выше утверждение даст нам зеленый свет, поскольку INF — это бесконечное значение.

Третье утверждение — assertNan() , которое не выполняется, если ожидаемое значение не является значением NAN .
Как вы, вероятно, знаете, NAN означает: не число , которое является результатом неверного вычисления в PHP, например, acos(8) .

 <?php class NanTest extends PHPUnit_Framework_Testcase { public function testNan() { $this->assertNan(14); } } 

Вышеприведенный тест также не пройден, поскольку фактическое значение равно 14 а ожидаемое значение — NAN .

Все вышеперечисленные методы принимают одинаковые аргументы: первый аргумент является фактическим значением, а второй (который является необязательным) — это сообщение, которое нужно напечатать, если утверждение не выполнено.

Глубокое клонирование пропущенных объектов

В версиях PHPUnit до 5.0 переданный объект (с помощью @depends ) передается как есть, то есть это ссылка на реальный объект (не клон). Рассмотрим следующий код:

 <?php class Foo { public $bars = []; } class Bar { public $name; function __construct($name) { $this->name = $name; } } class FooTest extends PHPUnit_Framework_TestCase { public function testProducer() { $foo = new Foo(); $foo->bars[] = new Bar('James'); $foo->bars[] = new Bar('Kirk'); $foo->bars[] = new Bar('Rob'); $this->assertEquals(3, count($foo->bars)); $this->assertEquals('Rob', $foo->bars[2]->name); return $foo; } /** * @depends testProducer */ public function testFirstDependent($foo) { $foo->bars[0]->name = 'Lars'; $this->assertEquals('Kirk', $foo->bars[1]->name); } /** * @depends testProducer */ public function testSecondDependent($foo) { $this->assertEquals('James', $foo->bars[0]->name); } } 

Вот история: у нас есть класс с именем Foo , который хранит экземпляры класса Bar .
В нашем тестовом классе у нас есть три метода теста: testProducer() , testFirstDependent() и testSecondDependent() . Последние два метода зависят от возвращаемого значения testProducer() .

При использовании @depends $foo передается по ссылке, что означает, что оба зависимых теста получают один и тот же экземпляр объекта. Это означает, что любые модификации объекта в одном из методов тестирования будут влиять на объект в другом методе тестирования.

Тем не менее, testFirstDependent() меняет $foo->bars[0]->name на James . Это приводит к testSecondDependent() , поскольку $foo->bars[0] уже была изменена testFirstDependent() .

Это результат теста в PHPUnit версии 4.8.24:

 PHPUnit 4.8.24 by Sebastian Bergmann and contributors. ..F Time: 75 ms, Memory: 5.00Mb There was 1 failure: 1) FooTest::testSecondDependent Failed asserting that two strings are equal. --- Expected +++ Actual @@ @@ -'James' +'Lars' /var/www/phpunit-example/tests/FooTest.php:47 FAILURES! Tests: 3, Assertions: 4, Failures: 1. 

Начиная с PHPUnit 5.0, переданные объекты могут быть глубоко клонированы с помощью @depends clone . Если мы запустим описанный выше тест (с аннотацией @depends clone в версии 5.0 и выше), мы получим зеленый свет, поскольку каждый тест модифицирует и утверждает свою собственную копию переданного объекта.

 <?php // ... class FooTest extends PHPUnit_Framework_TestCase { public function testProducer() { $foo = new Foo(); $foo->bars[] = new Bar('James'); $foo->bars[] = new Bar('Kirk'); $foo->bars[] = new Bar('Rob'); $this->assertEquals(3, count($foo->bars)); $this->assertEquals('Rob', $foo->bars[2]->name); return $foo; } /** * @depends clone testProducer */ public function testFirstDependent($foo) { $foo->bars[0]->name = 'Lars'; $this->assertEquals('Kirk', $foo->bars[1]->name); } /** * @depends clone testProducer */ public function testSecondDependent($foo) { $this->assertEquals('James', $foo->bars[0]->name); } } 

Вот результат теста в PHPUnit версии 5.3.2:

 PHPUnit 5.3.2 by Sebastian Bergmann and contributors. ... 3 / 3 (100%) Time: 86 ms, Memory: 3.50Mb OK (3 tests, 4 assertions) 

Передача ложных объектов с ожиданиями

Начиная с PHPUnit 5.0, мы можем передавать фиктивные объекты вместе с их ожиданиями ( ожидание определяет, когда и как вызывается определенный метод) в зависимые методы тестирования. Это не совсем возможно в версиях PHPUnit до версии 5.0. Также мы можем передавать фиктивные объекты в зависимые методы тестирования, но без каких-либо ожиданий. Рассмотрим следующий пример:

 <?php class fooTest extends PHPUnit_Framework_TestCase { public function testProducer() { $fooMock = $this->getMock('Foo'); $fooMock->expects($this->once()) ->methods('bar') ->will($this->returnValue(true)); return $fooMock; } /** * @depends clone testProducer */ public function testDependant($mock) { // This will fail in versions prior to 5.0.0 as the actual value will be NULL $this->assertTrue(true, $mock->bar()) } } 

Вышеприведенный тест не будет выполнен в версиях PHPUnit до 5.0, потому что мы ожидаем true тогда как фактическое значение равно null Причина в том, что метод testProducer возвращает фиктивный объект без ожидания, то есть все методы будут возвращать null .

 PHPUnit 4.8.24 by Sebastian Bergmann and contributors. .F Time: 74 ms, Memory: 5.00Mb There was 1 failure: 1) MockTest::testDependent Failed asserting that null is true. /var/www/phpunit-example/tests/MockTest.php:31 FAILURES! Tests: 2, Assertions: 2, Failures: 1. 

PHPUnit 5 даст нам зеленый свет, так как все макеты пройдены вместе с их ожиданиями.

 PHPUnit 5.3.2 by Sebastian Bergmann and contributors. .. 2 / 2 (100%) Time: 57 ms, Memory: 3.75Mb OK (2 tests, 2 assertions) 

Файл белого списка для покрытия кода теперь обязателен

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

 PHPUnit 4.8.24 by Sebastian Bergmann and contributors. Warning: No whitelist configured for code coverage ........... Time: 2.42 seconds, Memory: 15.50Mb OK (11 tests, 27 assertions) Generating code coverage reports in HTML format ... done 

Начиная с версии 5.0 мы получаем ошибку, и покрытие кода не создается:

 PHPUnit 5.3.2 by Sebastian Bergmann and contributors. Error: No whitelist configured, no code coverage will be generated 

Мы всегда должны настраивать белый список в phpunit.xml :

 <filter> <whitelist processUncoveredFilesFromWhitelist="true"> <directory suffix=".php">/path/to/files</directory> <file>/path/to/file</file> <exclude> <directory suffix=".php">/path/to/files</directory> <file>/path/to/file</file> </exclude> </whitelist> </filter> 

Удобный способ создания безконструктивных макетов

В PHPUnit 5 мы можем напрямую создать макет с отключенным конструктором, используя getMockWithoutInvokingTheOriginalConstructor() . Этот метод фактически является оберткой для disableOriginalConstructor() :

 <?php public function getMockWithoutInvokingTheOriginalConstructor($originalClassName) { return $this->getMockBuilder($originalClassName) ->disableOriginalConstructor() ->getMock(); } 

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

 <?php class MockTest extends PHPUnit_Framework_TestCase { // ... protected $mock; public function setUp() { $this->mock = $this->getMockWithoutInvokingTheOriginalConstructor('Foo'); $this->mock->expects($this->any()) ->method('bar') ->will($this->returnValue(true)); } // ... } 

Новые параметры в командной строке

PHPUnit 5 представил несколько опций в командной строке. Одним из этих параметров является --reverse-list , который позволяет сортировать неудачи теста в обратном порядке, поэтому первый сбой печатается внизу. Эта опция очень удобна, когда несколько тестов зависят от одного пройденного теста, и если мы исправим первый, остальные могут быть разрешены автоматически.

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

 phpunit --whitelist /path/to/php/files 

Есть еще один вариант, который может быть --atleast-version в некоторых случаях: --atleast-version .
Эта опция принимает число как минимальную версию PHPUnit и проверяет, больше ли текущая версия, чем это значение. Мы можем использовать его для запуска тестов, только если установлена ​​определенная версия PHPUnit:

 phpunit --atleast-version 5.3 && phpunit --whitelist path/to/php/files 

Удаленная функциональность

Функциональность, помеченная как устаревшая в PHPUnit 4, была удалена в версии 5.

Следующие утверждения были удалены:

  • assertSelectCount()
  • assertSelectRegExp()
  • assertSelectEquals()
  • assertTag()
  • assertNotTag()

--strict командной строки --strict и strict атрибут в файле конфигурации больше не существуют.

PHPUnit_Selenium больше не входит в дистрибутивы PHAR, и все связанные настройки в файле конфигурации были удалены.

Чтобы увидеть полный список добавленных, измененных и удаленных функций и исправлений ошибок, вы можете посетить эту страницу .

Завершение

PHPUnit кардинально изменил некоторые функции и, таким образом, запутал некоторых разработчиков, которые не были к этому готовы — мы надеемся, что наша статья поможет прояснить ситуацию. Несколько утверждений были добавлены в стек, а некоторые другие устарели. В PHPUnit 5 прекращена поддержка версий PHP до 5.6. PHPUnit 6 запланирован на 3 февраля 2017 года и больше не будет поддерживать PHP 5.

Вы уже обновились до PHPUnit 5? Если у вас есть какие-либо комментарии или предложения, или если что-то мы пропустили в этой статье, пожалуйста, сообщите нам об этом в комментариях!