diff --git a/CHANGELOG.md b/CHANGELOG.md index 09ff8801..e06c3572 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com), and this ### Changed * Use new reusable workflow to publish docker image +* [#2015](https://github.com/shlinkio/shlink/issues/2015) Update to PHPUnit 11. ### Deprecated * *Nothing* diff --git a/composer.json b/composer.json index 517caf0c..48ea06aa 100644 --- a/composer.json +++ b/composer.json @@ -28,7 +28,7 @@ "guzzlehttp/guzzle": "^7.5", "jaybizzle/crawler-detect": "^1.2.116", "laminas/laminas-config": "^3.8", - "laminas/laminas-config-aggregator": "^1.13", + "laminas/laminas-config-aggregator": "^1.15", "laminas/laminas-diactoros": "^3.3", "laminas/laminas-inputfilter": "^2.27", "laminas/laminas-servicemanager": "^3.21", @@ -67,9 +67,9 @@ "phpstan/phpstan-doctrine": "^1.3", "phpstan/phpstan-phpunit": "^1.3", "phpstan/phpstan-symfony": "^1.3", - "phpunit/php-code-coverage": "^10.1", - "phpunit/phpcov": "^9.0", - "phpunit/phpunit": "^10.4", + "phpunit/php-code-coverage": "^11.0", + "phpunit/phpcov": "^10.0", + "phpunit/phpunit": "^11.1", "roave/security-advisories": "dev-master", "shlinkio/php-coding-standard": "~2.3.0", "shlinkio/shlink-test-utils": "^4.1", diff --git a/module/CLI/test/Command/Integration/MatomoSendVisitsCommandTest.php b/module/CLI/test/Command/Integration/MatomoSendVisitsCommandTest.php index e3a52733..78d2f828 100644 --- a/module/CLI/test/Command/Integration/MatomoSendVisitsCommandTest.php +++ b/module/CLI/test/Command/Integration/MatomoSendVisitsCommandTest.php @@ -34,8 +34,8 @@ class MatomoSendVisitsCommandTest extends TestCase } #[Test] - #[TestWith([true])] - #[TestWith([false])] + #[TestWith([true], 'interactive')] + #[TestWith([false], 'not interactive')] public function warningIsOnlyDisplayedInInteractiveMode(bool $interactive): void { $this->visitSender->method('sendVisitsInDateRange')->willReturn(new SendVisitsResult()); diff --git a/module/CLI/test/Util/CliTestUtils.php b/module/CLI/test/Util/CliTestUtils.php index 9c92f882..c18186ba 100644 --- a/module/CLI/test/Util/CliTestUtils.php +++ b/module/CLI/test/Util/CliTestUtils.php @@ -25,6 +25,7 @@ class CliTestUtils $command = $generator->testDouble( Command::class, mockObject: true, + markAsMockObject: true, callOriginalConstructor: false, callOriginalClone: false, cloneArguments: false, diff --git a/module/Core/test/ErrorHandler/NotFoundRedirectHandlerTest.php b/module/Core/test/ErrorHandler/NotFoundRedirectHandlerTest.php index d8a0390d..53642f3a 100644 --- a/module/Core/test/ErrorHandler/NotFoundRedirectHandlerTest.php +++ b/module/Core/test/ErrorHandler/NotFoundRedirectHandlerTest.php @@ -10,6 +10,7 @@ use PHPUnit\Framework\Assert; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\MockObject\Rule\InvokedCount as InvokedCountMatcher; use PHPUnit\Framework\TestCase; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\UriInterface; @@ -60,14 +61,19 @@ class NotFoundRedirectHandlerTest extends TestCase public static function provideNonRedirectScenarios(): iterable { + $exactly = static fn (int $expectedCount) => new InvokedCountMatcher($expectedCount); + $once = static fn () => $exactly(1); + yield 'no domain' => [function ( MockObject&DomainServiceInterface $domainService, MockObject&NotFoundRedirectResolverInterface $resolver, + ) use ( + $once, ): void { - $domainService->expects(self::once())->method('findByAuthority')->withAnyParameters()->willReturn( + $domainService->expects($once())->method('findByAuthority')->withAnyParameters()->willReturn( null, ); - $resolver->expects(self::once())->method('resolveRedirectResponse')->with( + $resolver->expects($once())->method('resolveRedirectResponse')->with( self::isInstanceOf(NotFoundType::class), self::isInstanceOf(NotFoundRedirectOptions::class), self::isInstanceOf(UriInterface::class), @@ -76,12 +82,15 @@ class NotFoundRedirectHandlerTest extends TestCase yield 'non-redirecting domain' => [function ( MockObject&DomainServiceInterface $domainService, MockObject&NotFoundRedirectResolverInterface $resolver, + ) use ( + $once, + $exactly, ): void { - $domainService->expects(self::once())->method('findByAuthority')->withAnyParameters()->willReturn( + $domainService->expects($once())->method('findByAuthority')->withAnyParameters()->willReturn( Domain::withAuthority(''), ); $callCount = 0; - $resolver->expects(self::exactly(2))->method('resolveRedirectResponse')->willReturnCallback( + $resolver->expects($exactly(2))->method('resolveRedirectResponse')->willReturnCallback( function (mixed $arg1, mixed $arg2, mixed $arg3) use (&$callCount) { Assert::assertInstanceOf(NotFoundType::class, $arg1); Assert::assertInstanceOf($callCount === 0 ? Domain::class : NotFoundRedirectOptions::class, $arg2); diff --git a/module/Core/test/EventDispatcher/RabbitMq/NotifyVisitToRabbitMqTest.php b/module/Core/test/EventDispatcher/RabbitMq/NotifyVisitToRabbitMqTest.php index 7386169f..2251d301 100644 --- a/module/Core/test/EventDispatcher/RabbitMq/NotifyVisitToRabbitMqTest.php +++ b/module/Core/test/EventDispatcher/RabbitMq/NotifyVisitToRabbitMqTest.php @@ -10,6 +10,7 @@ use Exception; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\MockObject\Rule\InvokedCount as InvokedCountMatcher; use PHPUnit\Framework\TestCase; use Psr\Log\LoggerInterface; use RuntimeException; @@ -146,30 +147,34 @@ class NotifyVisitToRabbitMqTest extends TestCase public static function providePayloads(): iterable { + $exactly = static fn (int $expectedCount) => new InvokedCountMatcher($expectedCount); + $once = static fn () => $exactly(1); + $never = static fn () => $exactly(0); + yield 'non-orphan visit' => [ Visit::forValidShortUrl(ShortUrl::withLongUrl('https://longUrl'), Visitor::emptyInstance()), - function (MockObject & PublishingUpdatesGeneratorInterface $updatesGenerator): void { + function (MockObject & PublishingUpdatesGeneratorInterface $updatesGenerator) use ($once, $never): void { $update = Update::forTopicAndPayload('', []); - $updatesGenerator->expects(self::never())->method('newOrphanVisitUpdate'); - $updatesGenerator->expects(self::once())->method('newVisitUpdate')->withAnyParameters()->willReturn( + $updatesGenerator->expects($never())->method('newOrphanVisitUpdate'); + $updatesGenerator->expects($once())->method('newVisitUpdate')->withAnyParameters()->willReturn( $update, ); - $updatesGenerator->expects(self::once())->method('newShortUrlVisitUpdate')->willReturn($update); + $updatesGenerator->expects($once())->method('newShortUrlVisitUpdate')->willReturn($update); }, - function (MockObject & PublishingHelperInterface $helper): void { - $helper->expects(self::exactly(2))->method('publishUpdate')->with(self::isInstanceOf(Update::class)); + function (MockObject & PublishingHelperInterface $helper) use ($exactly): void { + $helper->expects($exactly(2))->method('publishUpdate')->with(self::isInstanceOf(Update::class)); }, ]; yield 'orphan visit' => [ Visit::forBasePath(Visitor::emptyInstance()), - function (MockObject & PublishingUpdatesGeneratorInterface $updatesGenerator): void { + function (MockObject & PublishingUpdatesGeneratorInterface $updatesGenerator) use ($once, $never): void { $update = Update::forTopicAndPayload('', []); - $updatesGenerator->expects(self::once())->method('newOrphanVisitUpdate')->willReturn($update); - $updatesGenerator->expects(self::never())->method('newVisitUpdate'); - $updatesGenerator->expects(self::never())->method('newShortUrlVisitUpdate'); + $updatesGenerator->expects($once())->method('newOrphanVisitUpdate')->willReturn($update); + $updatesGenerator->expects($never())->method('newVisitUpdate'); + $updatesGenerator->expects($never())->method('newShortUrlVisitUpdate'); }, - function (MockObject & PublishingHelperInterface $helper): void { - $helper->expects(self::once())->method('publishUpdate')->with(self::isInstanceOf(Update::class)); + function (MockObject & PublishingHelperInterface $helper) use ($once): void { + $helper->expects($once())->method('publishUpdate')->with(self::isInstanceOf(Update::class)); }, ]; }