Не смотря на то, что у PHPUnit хорошая документация, есть моменты, по которым хочется видеть больше примеров.
Мы хотим видеть все ошибки PHP, предупреждения, уведомления и т.д., когда мы проводим наши тесты.
error_reporting=-1
Когда что-то идет действительно не так, мы хотим видеть все сообщение об ошибке (которое по умолчанию усечено до 1024 символов).
log_errors_max_len=0
Мы не хотим, чтобы Xdebug печатал свои следы исключений во время выполнения наших тестов.
xdebug.show_exception_trace=0
Чтобы операторы assert() оценивались и вызывали исключения:
zend.assertions=1
assert.exception=1
Сбор данных о покрытии кода и генерация отчета о покрытии кода иногда требуют больше памяти, чем PHP может использовать по умолчанию.
memory_limit=-1
$mockWithOneRealMethod = $this->getMockBuilder(My::class)
->disableOriginalConstructor()
->setMethodsExcept(['myRealMethod'])
->getMock();
self::assertEquals($expected, $mockWithOneRealMethod->myRealMethod());Если myRealMethod вызывает другой публичный замоканный метод (например getPhone), который возвращает значение, то эмулировать возвращаемое им значение можно так:
$mockWithOneRealMethod->method('getPhone')->willReturn('8-800-123-4567');Как выбросить эксепшен из замоканного метода:
$mockWithOneRealMethod->method('getPhone')->willThrowException(new Exception('msg'));В PHPUnit 10:
1. setMethods и setMethodsExcept окончательно удалены, поэтому setMethodsExcept нужно написать самостоятельно с использованием onlyMethods
2. setMethods() разделили на части:
3. Теперь createPartialMock умеет заменять только существующие методы мокаемого класса, и нужно заменить использование createPartialMock на getMockBuilder()->addMethods().
4. Вместо собственной реализации подмены, теперь используется библиотека https://github.com/phpspec/prophecy - с использованием философии пророчеств (prophecies) и откровений (revelations). Подробнее: https://phpunit.readthedocs.io/ru/latest/test-doubles.html#prophecy
К сожалению, PHPUnit не позволяет менять доступ метода, поэтому у Вас 2 выхода:
$mock = $this->createMock(My::class);
$mock->method('mockMethod')->willReturnArgument(0); // 0 - первый аргумент$mock = $this->createMock(My::class);
$mock->method('mockMethod')->willReturnCallback(function ($value) {
return [ $value ];
});Прежде чем читать примеры, важно знать про следующее:
$mock->expects($this->once()) - метод должен быть вызван (не менее 1 раза и не более 1 раза), есть кстати :
$mock->expects($this->exactly(2)) - метод должен быть вызван указанное кол-во раз
$mock->expects($this->any()) - метод может быть вызван сколько угодно раз (а может и не быть вызван, тут нет требования), в итоге даже, если метод был вызван, а аргумент был успешно проверен, phpunit все равно скажет "This test did not perform any assertions", но если аргумент не прошел проверку, то phpunit ругнется, словно был сделан assert.
Важный момент:
например обычно мы вызываем myRealMethod а затем ожидаем совпадение результата:
$mock = $this->getMockBuilder(My::class)
->disableOriginalConstructor()
->setMethodsExcept(['myRealMethod'])
->getMock();
$result = $mock->myRealMethod();
$this->assertSame('expected value', $result);а в данном случае надо писать ожидание, а затем вызов метода:
$mock = $this->getMockBuilder(My::class)
->disableOriginalConstructor()
->setMethodsExcept(['myRealMethod'])
->getMock();
$mock->expects($this->once())->method('mockMethod')->with('expectedValue1', 'expectedValue2');
$mock->myRealMethod();Таким образом проверяет значение которое будет отправлено в mockMethod при вызове метода myRealMethod.
Еще примеры:
// Метод update() должен вызваться только один раз со строкой 'something' в качестве своего параметра.
$mock->expects($this->once())->method('update')->with('something');
// или
$mock->expects($this->once())->method('update')->with($this->equalTo('something'));
// Метод update() должен вызваться только два раза с определенными аргументами
$mock->expects($this->exactly(2)->method('update')->withConsecutive(
[$this->equalTo('foo'), $this->greaterThan(0)],
[$this->equalTo('bar'), $this->greaterThan(0)]
);Проверка через кэлбэк:
// Метод writeln должен быть вызыван с указанным значением аргумента
$phpFpmWrapper = $this->getMockBuilder(PhpFpmWrapper::class)
->disableOriginalConstructor()
->setMethods(['writeln'])
->getMock();
$result = '';
$phpFpmWrapper->method('writeln')->with($this->callback(function ($argument) use (&$result) {
$result = $argument;
return true; // иначе phpunit ругается
}));
$phpFpmWrapper->exec();
$this->assertSame('expected value', $result);$handler = $this->getMockBuilder(MyHandler::class)->setMethodsExcept(['realMethod'])->getMock();
$handler->expects(self::never())->method('someMethod');
$handler->realMethod();Более сложные варианты описаны тут »
if ($expected === UnexpectedValueException::class) {
$this->expectException($expected);
$handler->realMethod();
} else {
$result = $handler->realMethod();
$this->assertEquals($expected, $result, $message);
}Когда мы пишем функциональные тесты, то по окончанию теста нужно чистить EntityManager:
$entityManager->close();
внутри вызывается $entityManager->clear();
Казалось бы этого достаточно, но память все равно утекает, поэтому нужно делать:
self::$dbObjectManager = null;
Итоговая версия выглядит так:
public static function tearDownAfterClass(): void
{
parent::tearDownAfterClass();
self::$dbObjectManager->close();
self::$dbObjectManager = null;
} /**
* @var ContainerInterface
*/
private $container;
protected function setUp()
{
self::bootKernel();
$this->container = self::$kernel->getContainer();
$mock = $this->getMockBuilder(My::class)->disableOriginalConstructor()->getMock();
$this->container->set('my_service', $mock);
}PHPUnit Best Practices: 1
Какие файлы исходного кода следует включить в отчёт о покрытии кода (это можно сделать либо используя опцию командной строки --whitelist, либо через файл конфигурации). Сразу стоит сказать, что речь идет только о файлах из <whitelist>...</whitelist>
addUncoveredFilesFromWhitelist="false" - в отчёт попадают только whitelist-файлы содержащие хотя бы одну строку выполненного кодаaddUncoveredFilesFromWhitelist="true" (по умолчанию) - в отчёт попадают все whitelist-файлы, даже если ни одна строка кода такого файла не была выполненаprocessUncoveredFilesFromWhitelist="true" - PHPUnit выполнит include каждого whitelist-файла, таким образом комментарии в коде не будут влиять на % покрытия кода тестами. ВНИМАНИЕ: данный режим может вызвать проблемы, например, когда файл исходного кода содержит код вне области класса или функции.processUncoveredFilesFromWhitelist="false" (по умолчанию) - PHPUnit не будет делать include, таким образом комментарии в коде будут расценены как код и это скажется на уменьшении % покрытия кода тестами. Внимание: должно быть установлено addUncoveredFilesFromWhitelist="true"Источник: 1