Этот пост был рецензирован Клаудио Рибейро и Томасом Пунтом . Большое спасибо рецензентам, которые постоянно поддерживают контент 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? Если у вас есть какие-либо комментарии или предложения, или если что-то мы пропустили в этой статье, пожалуйста, сообщите нам об этом в комментариях!