From 58db902084f16c0bdef3aaf655b6e860793e1e33 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sat, 22 Oct 2022 12:46:16 +0200 Subject: [PATCH 01/27] Migrated CliTestUtilsTrait to use PHPUnit mocks --- module/CLI/test/CliTestUtilsTrait.php | 24 +++++++++---------- .../Command/Visit/LocateVisitsCommandTest.php | 17 +++++++++---- .../test/Factory/ApplicationFactoryTest.php | 4 ++-- 3 files changed, 25 insertions(+), 20 deletions(-) diff --git a/module/CLI/test/CliTestUtilsTrait.php b/module/CLI/test/CliTestUtilsTrait.php index ec7dd9d9..e69eb71e 100644 --- a/module/CLI/test/CliTestUtilsTrait.php +++ b/module/CLI/test/CliTestUtilsTrait.php @@ -4,9 +4,9 @@ declare(strict_types=1); namespace ShlinkioTest\Shlink\CLI; -use Prophecy\Argument; +use PHPUnit\Framework\Assert; +use PHPUnit\Framework\MockObject\MockObject; use Prophecy\PhpUnit\ProphecyTrait; -use Prophecy\Prophecy\ObjectProphecy; use Symfony\Component\Console\Application; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputDefinition; @@ -14,21 +14,19 @@ use Symfony\Component\Console\Tester\CommandTester; trait CliTestUtilsTrait { - use ProphecyTrait; + use ProphecyTrait; // TODO Remove /** - * @return ObjectProphecy|Command + * @return MockObject & Command */ - private function createCommandMock(string $name): ObjectProphecy + private function createCommandMock(string $name): MockObject { - $command = $this->prophesize(Command::class); - $command->getName()->willReturn($name); - $command->getDefinition()->willReturn($name); - $command->isEnabled()->willReturn(true); - $command->getAliases()->willReturn([]); - $command->getDefinition()->willReturn(new InputDefinition()); - $command->setApplication(Argument::type(Application::class))->willReturn(function (): void { - }); + $command = $this->createMock(Command::class); + $command->method('getName')->willReturn($name); + $command->method('isEnabled')->willReturn(true); + $command->method('getAliases')->willReturn([]); + $command->method('getDefinition')->willReturn(new InputDefinition()); + $command->method('setApplication')->with(Assert::isInstanceOf(Application::class)); return $command; } diff --git a/module/CLI/test/Command/Visit/LocateVisitsCommandTest.php b/module/CLI/test/Command/Visit/LocateVisitsCommandTest.php index 2c866689..0db2f493 100644 --- a/module/CLI/test/Command/Visit/LocateVisitsCommandTest.php +++ b/module/CLI/test/Command/Visit/LocateVisitsCommandTest.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace ShlinkioTest\Shlink\CLI\Command\Visit; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Prophecy\Argument; use Prophecy\Prophecy\ObjectProphecy; @@ -38,7 +39,7 @@ class LocateVisitsCommandTest extends TestCase private ObjectProphecy $visitService; private ObjectProphecy $visitToLocation; private ObjectProphecy $lock; - private ObjectProphecy $downloadDbCommand; + private MockObject $downloadDbCommand; protected function setUp(): void { @@ -59,9 +60,7 @@ class LocateVisitsCommandTest extends TestCase ); $this->downloadDbCommand = $this->createCommandMock(DownloadGeoLiteDbCommand::NAME); - $this->downloadDbCommand->run(Argument::cetera())->willReturn(ExitCodes::EXIT_SUCCESS); - - $this->commandTester = $this->testerForCommand($command, $this->downloadDbCommand->reveal()); + $this->commandTester = $this->testerForCommand($command, $this->downloadDbCommand); } /** @@ -87,6 +86,7 @@ class LocateVisitsCommandTest extends TestCase $resolveIpLocation = $this->visitToLocation->resolveVisitLocation(Argument::any())->willReturn( Location::emptyInstance(), ); + $this->downloadDbCommand->method('run')->withAnyParameters()->willReturn(ExitCodes::EXIT_SUCCESS); $this->commandTester->setInputs(['y']); $this->commandTester->execute($args); @@ -126,6 +126,7 @@ class LocateVisitsCommandTest extends TestCase $this->invokeHelperMethods($visit, $location), ); $resolveIpLocation = $this->visitToLocation->resolveVisitLocation(Argument::any())->willThrow($e); + $this->downloadDbCommand->method('run')->withAnyParameters()->willReturn(ExitCodes::EXIT_SUCCESS); $this->commandTester->execute([], ['verbosity' => OutputInterface::VERBOSITY_VERBOSE]); @@ -154,6 +155,7 @@ class LocateVisitsCommandTest extends TestCase $resolveIpLocation = $this->visitToLocation->resolveVisitLocation(Argument::any())->willThrow( IpCannotBeLocatedException::forError(WrongIpException::fromIpAddress('1.2.3.4')), ); + $this->downloadDbCommand->method('run')->withAnyParameters()->willReturn(ExitCodes::EXIT_SUCCESS); $this->commandTester->execute([], ['verbosity' => OutputInterface::VERBOSITY_VERBOSE]); @@ -183,6 +185,7 @@ class LocateVisitsCommandTest extends TestCase $locateVisits = $this->visitService->locateUnlocatedVisits(Argument::cetera())->will(function (): void { }); $resolveIpLocation = $this->visitToLocation->resolveVisitLocation(Argument::any()); + $this->downloadDbCommand->method('run')->withAnyParameters()->willReturn(ExitCodes::EXIT_SUCCESS); $this->commandTester->execute([], ['verbosity' => OutputInterface::VERBOSITY_VERBOSE]); $output = $this->commandTester->getDisplay(); @@ -198,7 +201,7 @@ class LocateVisitsCommandTest extends TestCase /** @test */ public function showsProperMessageWhenGeoLiteUpdateFails(): void { - $this->downloadDbCommand->run(Argument::cetera())->willReturn(ExitCodes::EXIT_FAILURE); + $this->downloadDbCommand->method('run')->withAnyParameters()->willReturn(ExitCodes::EXIT_FAILURE); $this->commandTester->execute([]); $output = $this->commandTester->getDisplay(); @@ -210,6 +213,8 @@ class LocateVisitsCommandTest extends TestCase /** @test */ public function providingAllFlagOnItsOwnDisplaysNotice(): void { + $this->downloadDbCommand->method('run')->withAnyParameters()->willReturn(ExitCodes::EXIT_SUCCESS); + $this->commandTester->execute(['--all' => true]); $output = $this->commandTester->getDisplay(); @@ -222,6 +227,8 @@ class LocateVisitsCommandTest extends TestCase */ public function processingAllCancelsCommandIfUserDoesNotActivelyAgreeToConfirmation(array $inputs): void { + $this->downloadDbCommand->method('run')->withAnyParameters()->willReturn(ExitCodes::EXIT_SUCCESS); + $this->expectException(RuntimeException::class); $this->expectExceptionMessage('Execution aborted'); diff --git a/module/CLI/test/Factory/ApplicationFactoryTest.php b/module/CLI/test/Factory/ApplicationFactoryTest.php index cb08a692..c93731c1 100644 --- a/module/CLI/test/Factory/ApplicationFactoryTest.php +++ b/module/CLI/test/Factory/ApplicationFactoryTest.php @@ -31,8 +31,8 @@ class ApplicationFactoryTest extends TestCase 'baz' => 'baz', ], ]); - $sm->setService('foo', $this->createCommandMock('foo')->reveal()); - $sm->setService('bar', $this->createCommandMock('bar')->reveal()); + $sm->setService('foo', $this->createCommandMock('foo')); + $sm->setService('bar', $this->createCommandMock('bar')); $instance = ($this->factory)($sm); From 2b7b5e9a8f237adcd12f4537afd80a86e8d93916 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sat, 22 Oct 2022 12:48:17 +0200 Subject: [PATCH 02/27] Migrated DisableKeyCommandTest to use PHPUnit mocks --- .../test/Command/Api/DisableKeyCommandTest.php | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/module/CLI/test/Command/Api/DisableKeyCommandTest.php b/module/CLI/test/Command/Api/DisableKeyCommandTest.php index 41a4f982..78f5bfa6 100644 --- a/module/CLI/test/Command/Api/DisableKeyCommandTest.php +++ b/module/CLI/test/Command/Api/DisableKeyCommandTest.php @@ -4,8 +4,8 @@ declare(strict_types=1); namespace ShlinkioTest\Shlink\CLI\Command\Api; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; -use Prophecy\Prophecy\ObjectProphecy; use Shlinkio\Shlink\CLI\Command\Api\DisableKeyCommand; use Shlinkio\Shlink\Common\Exception\InvalidArgumentException; use Shlinkio\Shlink\Rest\Service\ApiKeyServiceInterface; @@ -17,19 +17,19 @@ class DisableKeyCommandTest extends TestCase use CliTestUtilsTrait; private CommandTester $commandTester; - private ObjectProphecy $apiKeyService; + private MockObject $apiKeyService; protected function setUp(): void { - $this->apiKeyService = $this->prophesize(ApiKeyServiceInterface::class); - $this->commandTester = $this->testerForCommand(new DisableKeyCommand($this->apiKeyService->reveal())); + $this->apiKeyService = $this->createMock(ApiKeyServiceInterface::class); + $this->commandTester = $this->testerForCommand(new DisableKeyCommand($this->apiKeyService)); } /** @test */ public function providedApiKeyIsDisabled(): void { $apiKey = 'abcd1234'; - $this->apiKeyService->disable($apiKey)->shouldBeCalledOnce(); + $this->apiKeyService->expects($this->once())->method('disable')->with($this->equalTo($apiKey)); $this->commandTester->execute([ 'apiKey' => $apiKey, @@ -44,7 +44,9 @@ class DisableKeyCommandTest extends TestCase { $apiKey = 'abcd1234'; $expectedMessage = 'API key "abcd1234" does not exist.'; - $disable = $this->apiKeyService->disable($apiKey)->willThrow(new InvalidArgumentException($expectedMessage)); + $this->apiKeyService->expects($this->once())->method('disable')->with( + $this->equalTo($apiKey), + )->willThrowException(new InvalidArgumentException($expectedMessage)); $this->commandTester->execute([ 'apiKey' => $apiKey, @@ -52,6 +54,5 @@ class DisableKeyCommandTest extends TestCase $output = $this->commandTester->getDisplay(); self::assertStringContainsString($expectedMessage, $output); - $disable->shouldHaveBeenCalledOnce(); } } From a4c34ff7beb16918e52689f9677fe9db2167233e Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sat, 22 Oct 2022 12:52:11 +0200 Subject: [PATCH 03/27] Migrated GenerateKeyCommandTest to use PHPUnit mocks --- .../Command/Api/GenerateKeyCommandTest.php | 32 +++++++++++-------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/module/CLI/test/Command/Api/GenerateKeyCommandTest.php b/module/CLI/test/Command/Api/GenerateKeyCommandTest.php index 6db8581b..02e704ee 100644 --- a/module/CLI/test/Command/Api/GenerateKeyCommandTest.php +++ b/module/CLI/test/Command/Api/GenerateKeyCommandTest.php @@ -5,9 +5,8 @@ declare(strict_types=1); namespace ShlinkioTest\Shlink\CLI\Command\Api; use Cake\Chronos\Chronos; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; -use Prophecy\Argument; -use Prophecy\Prophecy\ObjectProphecy; use Shlinkio\Shlink\CLI\ApiKey\RoleResolverInterface; use Shlinkio\Shlink\CLI\Command\Api\GenerateKeyCommand; use Shlinkio\Shlink\Rest\Entity\ApiKey; @@ -21,22 +20,25 @@ class GenerateKeyCommandTest extends TestCase use CliTestUtilsTrait; private CommandTester $commandTester; - private ObjectProphecy $apiKeyService; + private MockObject $apiKeyService; protected function setUp(): void { - $this->apiKeyService = $this->prophesize(ApiKeyServiceInterface::class); - $roleResolver = $this->prophesize(RoleResolverInterface::class); - $roleResolver->determineRoles(Argument::type(InputInterface::class))->willReturn([]); + $this->apiKeyService = $this->createMock(ApiKeyServiceInterface::class); + $roleResolver = $this->createMock(RoleResolverInterface::class); + $roleResolver->method('determineRoles')->with($this->isInstanceOf(InputInterface::class))->willReturn([]); - $command = new GenerateKeyCommand($this->apiKeyService->reveal(), $roleResolver->reveal()); + $command = new GenerateKeyCommand($this->apiKeyService, $roleResolver); $this->commandTester = $this->testerForCommand($command); } /** @test */ public function noExpirationDateIsDefinedIfNotProvided(): void { - $this->apiKeyService->create(null, null)->shouldBeCalledOnce()->willReturn(ApiKey::create()); + $this->apiKeyService->expects($this->once())->method('create')->with( + $this->isNull(), + $this->isNull(), + )->willReturn(ApiKey::create()); $this->commandTester->execute([]); $output = $this->commandTester->getDisplay(); @@ -47,9 +49,10 @@ class GenerateKeyCommandTest extends TestCase /** @test */ public function expirationDateIsDefinedIfProvided(): void { - $this->apiKeyService->create(Argument::type(Chronos::class), null)->shouldBeCalledOnce()->willReturn( - ApiKey::create(), - ); + $this->apiKeyService->expects($this->once())->method('create')->with( + $this->isInstanceOf(Chronos::class), + $this->isNull(), + )->willReturn(ApiKey::create()); $this->commandTester->execute([ '--expiration-date' => '2016-01-01', @@ -59,9 +62,10 @@ class GenerateKeyCommandTest extends TestCase /** @test */ public function nameIsDefinedIfProvided(): void { - $this->apiKeyService->create(null, Argument::type('string'))->shouldBeCalledOnce()->willReturn( - ApiKey::create(), - ); + $this->apiKeyService->expects($this->once())->method('create')->with( + $this->isNull(), + $this->isType('string'), + )->willReturn(ApiKey::create()); $this->commandTester->execute([ '--name' => 'Alice', From 4cdcad29df6f6ee9de96883092e27645ceb5e546 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sat, 22 Oct 2022 12:53:28 +0200 Subject: [PATCH 04/27] Migrated ListKeysCommandTest to use PHPUnit mocks --- module/CLI/test/Command/Api/ListKeysCommandTest.php | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/module/CLI/test/Command/Api/ListKeysCommandTest.php b/module/CLI/test/Command/Api/ListKeysCommandTest.php index c52f466f..39da39a6 100644 --- a/module/CLI/test/Command/Api/ListKeysCommandTest.php +++ b/module/CLI/test/Command/Api/ListKeysCommandTest.php @@ -5,8 +5,8 @@ declare(strict_types=1); namespace ShlinkioTest\Shlink\CLI\Command\Api; use Cake\Chronos\Chronos; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; -use Prophecy\Prophecy\ObjectProphecy; use Shlinkio\Shlink\CLI\Command\Api\ListKeysCommand; use Shlinkio\Shlink\Core\Domain\Entity\Domain; use Shlinkio\Shlink\Rest\ApiKey\Model\ApiKeyMeta; @@ -21,12 +21,12 @@ class ListKeysCommandTest extends TestCase use CliTestUtilsTrait; private CommandTester $commandTester; - private ObjectProphecy $apiKeyService; + private MockObject $apiKeyService; protected function setUp(): void { - $this->apiKeyService = $this->prophesize(ApiKeyServiceInterface::class); - $this->commandTester = $this->testerForCommand(new ListKeysCommand($this->apiKeyService->reveal())); + $this->apiKeyService = $this->createMock(ApiKeyServiceInterface::class); + $this->commandTester = $this->testerForCommand(new ListKeysCommand($this->apiKeyService)); } /** @@ -35,13 +35,14 @@ class ListKeysCommandTest extends TestCase */ public function returnsExpectedOutput(array $keys, bool $enabledOnly, string $expected): void { - $listKeys = $this->apiKeyService->listKeys($enabledOnly)->willReturn($keys); + $this->apiKeyService->expects($this->once())->method('listKeys')->with( + $this->equalTo($enabledOnly), + )->willReturn($keys); $this->commandTester->execute(['--enabled-only' => $enabledOnly]); $output = $this->commandTester->getDisplay(); self::assertEquals($expected, $output); - $listKeys->shouldHaveBeenCalledOnce(); } public function provideKeysAndOutputs(): iterable From 13431ff8cf851994860bba4f212b3005478f53c2 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sat, 22 Oct 2022 13:05:36 +0200 Subject: [PATCH 05/27] Migrated CreateDatabaseCommandTest to use PHPUnit mocks --- .../Command/Db/CreateDatabaseCommandTest.php | 134 ++++++++---------- 1 file changed, 59 insertions(+), 75 deletions(-) diff --git a/module/CLI/test/Command/Db/CreateDatabaseCommandTest.php b/module/CLI/test/Command/Db/CreateDatabaseCommandTest.php index f500775a..b335e19e 100644 --- a/module/CLI/test/Command/Db/CreateDatabaseCommandTest.php +++ b/module/CLI/test/Command/Db/CreateDatabaseCommandTest.php @@ -9,9 +9,8 @@ use Doctrine\DBAL\Driver; use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Platforms\SqlitePlatform; use Doctrine\DBAL\Schema\AbstractSchemaManager; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; -use Prophecy\Argument; -use Prophecy\Prophecy\ObjectProphecy; use Shlinkio\Shlink\CLI\Command\Db\CreateDatabaseCommand; use Shlinkio\Shlink\CLI\Util\ProcessRunnerInterface; use ShlinkioTest\Shlink\CLI\CliTestUtilsTrait; @@ -28,40 +27,37 @@ class CreateDatabaseCommandTest extends TestCase use CliTestUtilsTrait; private CommandTester $commandTester; - private ObjectProphecy $processHelper; - private ObjectProphecy $regularConn; - private ObjectProphecy $schemaManager; - private ObjectProphecy $driver; + private MockObject $processHelper; + private MockObject $regularConn; + private MockObject $schemaManager; + private MockObject $driver; protected function setUp(): void { - $locker = $this->prophesize(LockFactory::class); - $lock = $this->prophesize(LockInterface::class); - $lock->acquire(Argument::any())->willReturn(true); - $lock->release()->will(function (): void { - }); - $locker->createLock(Argument::cetera())->willReturn($lock->reveal()); + $locker = $this->createMock(LockFactory::class); + $lock = $this->createMock(LockInterface::class); + $lock->method('acquire')->withAnyParameters()->willReturn(true); + $locker->method('createLock')->withAnyParameters()->willReturn($lock); - $phpExecutableFinder = $this->prophesize(PhpExecutableFinder::class); - $phpExecutableFinder->find(false)->willReturn('/usr/local/bin/php'); + $phpExecutableFinder = $this->createMock(PhpExecutableFinder::class); + $phpExecutableFinder->method('find')->with($this->isFalse())->willReturn('/usr/local/bin/php'); - $this->processHelper = $this->prophesize(ProcessRunnerInterface::class); - $this->schemaManager = $this->prophesize(AbstractSchemaManager::class); + $this->processHelper = $this->createMock(ProcessRunnerInterface::class); + $this->schemaManager = $this->createMock(AbstractSchemaManager::class); - $this->regularConn = $this->prophesize(Connection::class); - $this->regularConn->createSchemaManager()->willReturn($this->schemaManager->reveal()); - $this->driver = $this->prophesize(Driver::class); - $this->regularConn->getDriver()->willReturn($this->driver->reveal()); - $this->driver->getDatabasePlatform()->willReturn($this->prophesize(AbstractPlatform::class)->reveal()); - $noDbNameConn = $this->prophesize(Connection::class); - $noDbNameConn->createSchemaManager()->willReturn($this->schemaManager->reveal()); + $this->regularConn = $this->createMock(Connection::class); + $this->regularConn->method('createSchemaManager')->willReturn($this->schemaManager); + $this->driver = $this->createMock(Driver::class); + $this->regularConn->method('getDriver')->willReturn($this->driver); + $noDbNameConn = $this->createMock(Connection::class); + $noDbNameConn->method('createSchemaManager')->withAnyParameters()->willReturn($this->schemaManager); $command = new CreateDatabaseCommand( - $locker->reveal(), - $this->processHelper->reveal(), - $phpExecutableFinder->reveal(), - $this->regularConn->reveal(), - $noDbNameConn->reveal(), + $locker, + $this->processHelper, + $phpExecutableFinder, + $this->regularConn, + $noDbNameConn, ); $this->commandTester = $this->testerForCommand($command); @@ -71,38 +67,33 @@ class CreateDatabaseCommandTest extends TestCase public function successMessageIsPrintedIfDatabaseAlreadyExists(): void { $shlinkDatabase = 'shlink_database'; - $getDatabase = $this->regularConn->getParams()->willReturn(['dbname' => $shlinkDatabase]); - $listDatabases = $this->schemaManager->listDatabases()->willReturn(['foo', $shlinkDatabase, 'bar']); - $createDatabase = $this->schemaManager->createDatabase($shlinkDatabase)->will(function (): void { - }); - $listTables = $this->schemaManager->listTableNames()->willReturn(['foo_table', 'bar_table']); + $this->regularConn->expects($this->once())->method('getParams')->willReturn(['dbname' => $shlinkDatabase]); + $this->schemaManager->expects($this->once())->method('listDatabases')->willReturn( + ['foo', $shlinkDatabase, 'bar'], + ); + $this->schemaManager->expects($this->never())->method('createDatabase'); + $this->schemaManager->expects($this->once())->method('listTableNames')->willReturn(['foo_table', 'bar_table']); + $this->driver->method('getDatabasePlatform')->willReturn($this->createMock(AbstractPlatform::class)); $this->commandTester->execute([]); $output = $this->commandTester->getDisplay(); self::assertStringContainsString('Database already exists. Run "db:migrate" command', $output); - $getDatabase->shouldHaveBeenCalledOnce(); - $listDatabases->shouldHaveBeenCalledOnce(); - $createDatabase->shouldNotHaveBeenCalled(); - $listTables->shouldHaveBeenCalledOnce(); } /** @test */ public function databaseIsCreatedIfItDoesNotExist(): void { $shlinkDatabase = 'shlink_database'; - $getDatabase = $this->regularConn->getParams()->willReturn(['dbname' => $shlinkDatabase]); - $listDatabases = $this->schemaManager->listDatabases()->willReturn(['foo', 'bar']); - $createDatabase = $this->schemaManager->createDatabase($shlinkDatabase)->will(function (): void { - }); - $listTables = $this->schemaManager->listTableNames()->willReturn(['foo_table', 'bar_table', MIGRATIONS_TABLE]); + $this->regularConn->expects($this->once())->method('getParams')->willReturn(['dbname' => $shlinkDatabase]); + $this->schemaManager->expects($this->once())->method('listDatabases')->willReturn(['foo', 'bar']); + $this->schemaManager->expects($this->once())->method('createDatabase')->with($this->equalTo($shlinkDatabase)); + $this->schemaManager->expects($this->once())->method('listTableNames')->willReturn( + ['foo_table', 'bar_table', MIGRATIONS_TABLE], + ); + $this->driver->method('getDatabasePlatform')->willReturn($this->createMock(AbstractPlatform::class)); $this->commandTester->execute([]); - - $getDatabase->shouldHaveBeenCalledOnce(); - $listDatabases->shouldHaveBeenCalledOnce(); - $createDatabase->shouldHaveBeenCalledOnce(); - $listTables->shouldHaveBeenCalledOnce(); } /** @@ -112,28 +103,28 @@ class CreateDatabaseCommandTest extends TestCase public function tablesAreCreatedIfDatabaseIsEmpty(array $tables): void { $shlinkDatabase = 'shlink_database'; - $getDatabase = $this->regularConn->getParams()->willReturn(['dbname' => $shlinkDatabase]); - $listDatabases = $this->schemaManager->listDatabases()->willReturn(['foo', $shlinkDatabase, 'bar']); - $createDatabase = $this->schemaManager->createDatabase($shlinkDatabase)->will(function (): void { - }); - $listTables = $this->schemaManager->listTableNames()->willReturn($tables); - $runCommand = $this->processHelper->run(Argument::type(OutputInterface::class), [ - '/usr/local/bin/php', - CreateDatabaseCommand::DOCTRINE_SCRIPT, - CreateDatabaseCommand::DOCTRINE_CREATE_SCHEMA_COMMAND, - '--no-interaction', - ]); + $this->regularConn->expects($this->once())->method('getParams')->willReturn(['dbname' => $shlinkDatabase]); + $this->schemaManager->expects($this->once())->method('listDatabases')->willReturn( + ['foo', $shlinkDatabase, 'bar'], + ); + $this->schemaManager->expects($this->never())->method('createDatabase'); + $this->schemaManager->expects($this->once())->method('listTableNames')->willReturn($tables); + $this->processHelper->expects($this->once())->method('run')->with( + $this->isInstanceOf(OutputInterface::class), + $this->equalTo([ + '/usr/local/bin/php', + CreateDatabaseCommand::DOCTRINE_SCRIPT, + CreateDatabaseCommand::DOCTRINE_CREATE_SCHEMA_COMMAND, + '--no-interaction', + ]), + ); + $this->driver->method('getDatabasePlatform')->willReturn($this->createMock(AbstractPlatform::class)); $this->commandTester->execute([]); $output = $this->commandTester->getDisplay(); self::assertStringContainsString('Creating database tables...', $output); self::assertStringContainsString('Database properly created!', $output); - $getDatabase->shouldHaveBeenCalledOnce(); - $listDatabases->shouldHaveBeenCalledOnce(); - $createDatabase->shouldNotHaveBeenCalled(); - $listTables->shouldHaveBeenCalledOnce(); - $runCommand->shouldHaveBeenCalledOnce(); } public function provideEmptyDatabase(): iterable @@ -145,20 +136,13 @@ class CreateDatabaseCommandTest extends TestCase /** @test */ public function databaseCheckIsSkippedForSqlite(): void { - $this->driver->getDatabasePlatform()->willReturn($this->prophesize(SqlitePlatform::class)->reveal()); + $this->driver->method('getDatabasePlatform')->willReturn($this->createMock(SqlitePlatform::class)); - $shlinkDatabase = 'shlink_database'; - $getDatabase = $this->regularConn->getParams()->willReturn(['dbname' => $shlinkDatabase]); - $listDatabases = $this->schemaManager->listDatabases()->willReturn(['foo', 'bar']); - $createDatabase = $this->schemaManager->createDatabase($shlinkDatabase)->will(function (): void { - }); - $listTables = $this->schemaManager->listTableNames()->willReturn(['foo_table', 'bar_table']); + $this->regularConn->expects($this->never())->method('getParams'); + $this->schemaManager->expects($this->never())->method('listDatabases'); + $this->schemaManager->expects($this->never())->method('createDatabase'); + $this->schemaManager->expects($this->once())->method('listTableNames')->willReturn(['foo_table', 'bar_table']); $this->commandTester->execute([]); - - $getDatabase->shouldNotHaveBeenCalled(); - $listDatabases->shouldNotHaveBeenCalled(); - $createDatabase->shouldNotHaveBeenCalled(); - $listTables->shouldHaveBeenCalledOnce(); } } From 101b4daff40711f2cafe98a5525a59608d05b066 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sat, 22 Oct 2022 13:08:05 +0200 Subject: [PATCH 06/27] Migrated MigrateDatabaseCommandTest to use PHPUnit mocks --- .../Command/Db/MigrateDatabaseCommandTest.php | 43 ++++++++----------- 1 file changed, 19 insertions(+), 24 deletions(-) diff --git a/module/CLI/test/Command/Db/MigrateDatabaseCommandTest.php b/module/CLI/test/Command/Db/MigrateDatabaseCommandTest.php index 1a8dfb0e..6390d044 100644 --- a/module/CLI/test/Command/Db/MigrateDatabaseCommandTest.php +++ b/module/CLI/test/Command/Db/MigrateDatabaseCommandTest.php @@ -4,9 +4,8 @@ declare(strict_types=1); namespace ShlinkioTest\Shlink\CLI\Command\Db; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; -use Prophecy\Argument; -use Prophecy\Prophecy\ObjectProphecy; use Shlinkio\Shlink\CLI\Command\Db\MigrateDatabaseCommand; use Shlinkio\Shlink\CLI\Util\ProcessRunnerInterface; use ShlinkioTest\Shlink\CLI\CliTestUtilsTrait; @@ -21,45 +20,41 @@ class MigrateDatabaseCommandTest extends TestCase use CliTestUtilsTrait; private CommandTester $commandTester; - private ObjectProphecy $processHelper; + private MockObject $processHelper; protected function setUp(): void { - $locker = $this->prophesize(LockFactory::class); - $lock = $this->prophesize(LockInterface::class); - $lock->acquire(Argument::any())->willReturn(true); - $lock->release()->will(function (): void { - }); - $locker->createLock(Argument::cetera())->willReturn($lock->reveal()); + $locker = $this->createMock(LockFactory::class); + $lock = $this->createMock(LockInterface::class); + $lock->method('acquire')->withAnyParameters()->willReturn(true); + $locker->method('createLock')->withAnyParameters()->willReturn($lock); - $phpExecutableFinder = $this->prophesize(PhpExecutableFinder::class); - $phpExecutableFinder->find(false)->willReturn('/usr/local/bin/php'); + $phpExecutableFinder = $this->createMock(PhpExecutableFinder::class); + $phpExecutableFinder->method('find')->with($this->isFalse())->willReturn('/usr/local/bin/php'); - $this->processHelper = $this->prophesize(ProcessRunnerInterface::class); + $this->processHelper = $this->createMock(ProcessRunnerInterface::class); - $command = new MigrateDatabaseCommand( - $locker->reveal(), - $this->processHelper->reveal(), - $phpExecutableFinder->reveal(), - ); + $command = new MigrateDatabaseCommand($locker, $this->processHelper, $phpExecutableFinder); $this->commandTester = $this->testerForCommand($command); } /** @test */ public function migrationsCommandIsRunWithProperVerbosity(): void { - $runCommand = $this->processHelper->run(Argument::type(OutputInterface::class), [ - '/usr/local/bin/php', - MigrateDatabaseCommand::DOCTRINE_MIGRATIONS_SCRIPT, - MigrateDatabaseCommand::DOCTRINE_MIGRATE_COMMAND, - '--no-interaction', - ]); + $this->processHelper->expects($this->once())->method('run')->with( + $this->isInstanceOf(OutputInterface::class), + $this->equalTo([ + '/usr/local/bin/php', + MigrateDatabaseCommand::DOCTRINE_MIGRATIONS_SCRIPT, + MigrateDatabaseCommand::DOCTRINE_MIGRATE_COMMAND, + '--no-interaction', + ]), + ); $this->commandTester->execute([]); $output = $this->commandTester->getDisplay(); self::assertStringContainsString('Migrating database...', $output); self::assertStringContainsString('Database properly migrated!', $output); - $runCommand->shouldHaveBeenCalledOnce(); } } From 960bdfc232bd468f39021d8327dec5412811eede Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sat, 22 Oct 2022 13:17:12 +0200 Subject: [PATCH 07/27] Migrated DomainRedirectsCommandTest to use PHPUnit mocks --- .../Domain/DomainRedirectsCommandTest.php | 81 +++++++++---------- 1 file changed, 39 insertions(+), 42 deletions(-) diff --git a/module/CLI/test/Command/Domain/DomainRedirectsCommandTest.php b/module/CLI/test/Command/Domain/DomainRedirectsCommandTest.php index 0f8c76af..5d9f5bd4 100644 --- a/module/CLI/test/Command/Domain/DomainRedirectsCommandTest.php +++ b/module/CLI/test/Command/Domain/DomainRedirectsCommandTest.php @@ -4,8 +4,8 @@ declare(strict_types=1); namespace ShlinkioTest\Shlink\CLI\Command\Domain; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; -use Prophecy\Prophecy\ObjectProphecy; use Shlinkio\Shlink\CLI\Command\Domain\DomainRedirectsCommand; use Shlinkio\Shlink\Core\Config\NotFoundRedirects; use Shlinkio\Shlink\Core\Domain\DomainServiceInterface; @@ -22,12 +22,12 @@ class DomainRedirectsCommandTest extends TestCase use CliTestUtilsTrait; private CommandTester $commandTester; - private ObjectProphecy $domainService; + private MockObject $domainService; protected function setUp(): void { - $this->domainService = $this->prophesize(DomainServiceInterface::class); - $this->commandTester = $this->testerForCommand(new DomainRedirectsCommand($this->domainService->reveal())); + $this->domainService = $this->createMock(DomainServiceInterface::class); + $this->commandTester = $this->testerForCommand(new DomainRedirectsCommand($this->domainService)); } /** @@ -37,11 +37,14 @@ class DomainRedirectsCommandTest extends TestCase public function onlyPlainQuestionsAreAskedForNewDomainsAndDomainsWithNoRedirects(?Domain $domain): void { $domainAuthority = 'my-domain.com'; - $findDomain = $this->domainService->findByAuthority($domainAuthority)->willReturn($domain); - $configureRedirects = $this->domainService->configureNotFoundRedirects( - $domainAuthority, - NotFoundRedirects::withRedirects('foo.com', null, 'baz.com'), + $this->domainService->expects($this->once())->method('findByAuthority')->with( + $this->equalTo($domainAuthority), + )->willReturn($domain); + $this->domainService->expects($this->once())->method('configureNotFoundRedirects')->with( + $this->equalTo($domainAuthority), + $this->equalTo(NotFoundRedirects::withRedirects('foo.com', null, 'baz.com')), )->willReturn(Domain::withAuthority('')); + $this->domainService->expects($this->never())->method('listDomains'); $this->commandTester->setInputs(['foo.com', '', 'baz.com']); $this->commandTester->execute(['domain' => $domainAuthority]); @@ -55,9 +58,6 @@ class DomainRedirectsCommandTest extends TestCase ); self::assertStringContainsString('URL to redirect to when a user hits an invalid short URL', $output); self::assertEquals(3, substr_count($output, '(Leave empty for no redirect)')); - $findDomain->shouldHaveBeenCalledOnce(); - $configureRedirects->shouldHaveBeenCalledOnce(); - $this->domainService->listDomains()->shouldNotHaveBeenCalled(); } public function provideDomains(): iterable @@ -73,11 +73,14 @@ class DomainRedirectsCommandTest extends TestCase $domain = Domain::withAuthority($domainAuthority); $domain->configureNotFoundRedirects(NotFoundRedirects::withRedirects('foo.com', 'bar.com', 'baz.com')); - $findDomain = $this->domainService->findByAuthority($domainAuthority)->willReturn($domain); - $configureRedirects = $this->domainService->configureNotFoundRedirects( - $domainAuthority, - NotFoundRedirects::withRedirects(null, 'edited.com', 'baz.com'), + $this->domainService->expects($this->once())->method('findByAuthority')->with( + $this->equalTo($domainAuthority), )->willReturn($domain); + $this->domainService->expects($this->once())->method('configureNotFoundRedirects')->with( + $this->equalTo($domainAuthority), + $this->equalTo(NotFoundRedirects::withRedirects(null, 'edited.com', 'baz.com')), + )->willReturn($domain); + $this->domainService->expects($this->never())->method('listDomains'); $this->commandTester->setInputs(['2', '1', 'edited.com', '0']); $this->commandTester->execute(['domain' => $domainAuthority]); @@ -90,9 +93,6 @@ class DomainRedirectsCommandTest extends TestCase self::assertStringNotContainsStringIgnoringCase('(Leave empty for no redirect)', $output); self::assertEquals(3, substr_count($output, 'Set new redirect URL')); self::assertEquals(3, substr_count($output, 'Remove redirect')); - $findDomain->shouldHaveBeenCalledOnce(); - $configureRedirects->shouldHaveBeenCalledOnce(); - $this->domainService->listDomains()->shouldNotHaveBeenCalled(); } /** @test */ @@ -101,11 +101,13 @@ class DomainRedirectsCommandTest extends TestCase $domainAuthority = 'example.com'; $domain = Domain::withAuthority($domainAuthority); - $listDomains = $this->domainService->listDomains()->willReturn([]); - $findDomain = $this->domainService->findByAuthority($domainAuthority)->willReturn($domain); - $configureRedirects = $this->domainService->configureNotFoundRedirects( - $domainAuthority, - NotFoundRedirects::withoutRedirects(), + $this->domainService->expects($this->once())->method('listDomains')->with()->willReturn([]); + $this->domainService->expects($this->once())->method('findByAuthority')->with( + $this->equalTo($domainAuthority), + )->willReturn($domain); + $this->domainService->expects($this->once())->method('configureNotFoundRedirects')->with( + $this->equalTo($domainAuthority), + $this->equalTo(NotFoundRedirects::withoutRedirects()), )->willReturn($domain); $this->commandTester->setInputs([$domainAuthority, '', '', '']); @@ -113,9 +115,6 @@ class DomainRedirectsCommandTest extends TestCase $output = $this->commandTester->getDisplay(); self::assertStringContainsString('Domain authority for which you want to set specific redirects', $output); - $listDomains->shouldHaveBeenCalledOnce(); - $findDomain->shouldHaveBeenCalledOnce(); - $configureRedirects->shouldHaveBeenCalledOnce(); } /** @test */ @@ -124,15 +123,17 @@ class DomainRedirectsCommandTest extends TestCase $domainAuthority = 'existing-two.com'; $domain = Domain::withAuthority($domainAuthority); - $listDomains = $this->domainService->listDomains()->willReturn([ + $this->domainService->expects($this->once())->method('listDomains')->with()->willReturn([ DomainItem::forDefaultDomain('default-domain.com', new NotFoundRedirectOptions()), DomainItem::forNonDefaultDomain(Domain::withAuthority('existing-one.com')), DomainItem::forNonDefaultDomain(Domain::withAuthority($domainAuthority)), ]); - $findDomain = $this->domainService->findByAuthority($domainAuthority)->willReturn($domain); - $configureRedirects = $this->domainService->configureNotFoundRedirects( - $domainAuthority, - NotFoundRedirects::withoutRedirects(), + $this->domainService->expects($this->once())->method('findByAuthority')->with( + $this->equalTo($domainAuthority), + )->willReturn($domain); + $this->domainService->expects($this->once())->method('configureNotFoundRedirects')->with( + $this->equalTo($domainAuthority), + $this->equalTo(NotFoundRedirects::withoutRedirects()), )->willReturn($domain); $this->commandTester->setInputs(['1', '', '', '']); @@ -143,9 +144,6 @@ class DomainRedirectsCommandTest extends TestCase self::assertStringNotContainsString('default-domain.com', $output); self::assertStringContainsString('existing-one.com', $output); self::assertStringContainsString($domainAuthority, $output); - $listDomains->shouldHaveBeenCalledOnce(); - $findDomain->shouldHaveBeenCalledOnce(); - $configureRedirects->shouldHaveBeenCalledOnce(); } /** @test */ @@ -154,15 +152,17 @@ class DomainRedirectsCommandTest extends TestCase $domainAuthority = 'new-domain.com'; $domain = Domain::withAuthority($domainAuthority); - $listDomains = $this->domainService->listDomains()->willReturn([ + $this->domainService->expects($this->once())->method('listDomains')->with()->willReturn([ DomainItem::forDefaultDomain('default-domain.com', new NotFoundRedirectOptions()), DomainItem::forNonDefaultDomain(Domain::withAuthority('existing-one.com')), DomainItem::forNonDefaultDomain(Domain::withAuthority('existing-two.com')), ]); - $findDomain = $this->domainService->findByAuthority($domainAuthority)->willReturn($domain); - $configureRedirects = $this->domainService->configureNotFoundRedirects( - $domainAuthority, - NotFoundRedirects::withoutRedirects(), + $this->domainService->expects($this->once())->method('findByAuthority')->with( + $this->equalTo($domainAuthority), + )->willReturn($domain); + $this->domainService->expects($this->once())->method('configureNotFoundRedirects')->with( + $this->equalTo($domainAuthority), + $this->equalTo(NotFoundRedirects::withoutRedirects()), )->willReturn($domain); $this->commandTester->setInputs(['2', $domainAuthority, '', '', '']); @@ -173,8 +173,5 @@ class DomainRedirectsCommandTest extends TestCase self::assertStringNotContainsString('default-domain.com', $output); self::assertStringContainsString('existing-one.com', $output); self::assertStringContainsString('existing-two.com', $output); - $listDomains->shouldHaveBeenCalledOnce(); - $findDomain->shouldHaveBeenCalledOnce(); - $configureRedirects->shouldHaveBeenCalledOnce(); } } From 3d358ab046cf275ccf2196f7ce730b0f7e67f7c9 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sat, 22 Oct 2022 13:21:00 +0200 Subject: [PATCH 08/27] Migrated GetDomainVisitsCommandTest to use PHPUnit mocks --- .../Domain/GetDomainVisitsCommandTest.php | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/module/CLI/test/Command/Domain/GetDomainVisitsCommandTest.php b/module/CLI/test/Command/Domain/GetDomainVisitsCommandTest.php index 85c5ef0c..a50138b6 100644 --- a/module/CLI/test/Command/Domain/GetDomainVisitsCommandTest.php +++ b/module/CLI/test/Command/Domain/GetDomainVisitsCommandTest.php @@ -5,9 +5,8 @@ declare(strict_types=1); namespace ShlinkioTest\Shlink\CLI\Command\Domain; use Pagerfanta\Adapter\ArrayAdapter; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; -use Prophecy\Argument; -use Prophecy\Prophecy\ObjectProphecy; use Shlinkio\Shlink\CLI\Command\Domain\GetDomainVisitsCommand; use Shlinkio\Shlink\Common\Paginator\Paginator; use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl; @@ -25,16 +24,16 @@ class GetDomainVisitsCommandTest extends TestCase use CliTestUtilsTrait; private CommandTester $commandTester; - private ObjectProphecy $visitsHelper; - private ObjectProphecy $stringifier; + private MockObject $visitsHelper; + private MockObject $stringifier; protected function setUp(): void { - $this->visitsHelper = $this->prophesize(VisitsStatsHelperInterface::class); - $this->stringifier = $this->prophesize(ShortUrlStringifierInterface::class); + $this->visitsHelper = $this->createMock(VisitsStatsHelperInterface::class); + $this->stringifier = $this->createMock(ShortUrlStringifierInterface::class); $this->commandTester = $this->testerForCommand( - new GetDomainVisitsCommand($this->visitsHelper->reveal(), $this->stringifier->reveal()), + new GetDomainVisitsCommand($this->visitsHelper, $this->stringifier), ); } @@ -46,10 +45,13 @@ class GetDomainVisitsCommandTest extends TestCase VisitLocation::fromGeolocation(new Location('', 'Spain', '', 'Madrid', 0, 0, '')), ); $domain = 'doma.in'; - $getVisits = $this->visitsHelper->visitsForDomain($domain, Argument::any())->willReturn( - new Paginator(new ArrayAdapter([$visit])), + $this->visitsHelper->expects($this->once())->method('visitsForDomain')->with( + $this->equalTo($domain), + $this->anything(), + )->willReturn(new Paginator(new ArrayAdapter([$visit]))); + $this->stringifier->expects($this->once())->method('stringify')->with($this->equalTo($shortUrl))->willReturn( + 'the_short_url', ); - $stringify = $this->stringifier->stringify($shortUrl)->willReturn('the_short_url'); $this->commandTester->execute(['domain' => $domain]); $output = $this->commandTester->getDisplay(); @@ -65,7 +67,5 @@ class GetDomainVisitsCommandTest extends TestCase OUTPUT, $output, ); - $getVisits->shouldHaveBeenCalledOnce(); - $stringify->shouldHaveBeenCalledOnce(); } } From d8be3c28cbdbb99f43f711d4ae7c8636a38a8d28 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sat, 22 Oct 2022 13:21:54 +0200 Subject: [PATCH 09/27] Migrated ListDomainsCommandTest to use PHPUnit mocks --- .../test/Command/Domain/ListDomainsCommandTest.php | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/module/CLI/test/Command/Domain/ListDomainsCommandTest.php b/module/CLI/test/Command/Domain/ListDomainsCommandTest.php index efaa25ed..ef8b276c 100644 --- a/module/CLI/test/Command/Domain/ListDomainsCommandTest.php +++ b/module/CLI/test/Command/Domain/ListDomainsCommandTest.php @@ -4,8 +4,8 @@ declare(strict_types=1); namespace ShlinkioTest\Shlink\CLI\Command\Domain; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; -use Prophecy\Prophecy\ObjectProphecy; use Shlinkio\Shlink\CLI\Command\Domain\ListDomainsCommand; use Shlinkio\Shlink\CLI\Util\ExitCodes; use Shlinkio\Shlink\Core\Config\NotFoundRedirects; @@ -21,12 +21,12 @@ class ListDomainsCommandTest extends TestCase use CliTestUtilsTrait; private CommandTester $commandTester; - private ObjectProphecy $domainService; + private MockObject $domainService; protected function setUp(): void { - $this->domainService = $this->prophesize(DomainServiceInterface::class); - $this->commandTester = $this->testerForCommand(new ListDomainsCommand($this->domainService->reveal())); + $this->domainService = $this->createMock(DomainServiceInterface::class); + $this->commandTester = $this->testerForCommand(new ListDomainsCommand($this->domainService)); } /** @@ -42,7 +42,7 @@ class ListDomainsCommandTest extends TestCase 'https://foo.com/baz-domain/invalid', )); - $listDomains = $this->domainService->listDomains()->willReturn([ + $this->domainService->expects($this->once())->method('listDomains')->with()->willReturn([ DomainItem::forDefaultDomain('foo.com', new NotFoundRedirectOptions( invalidShortUrl: 'https://foo.com/default/invalid', baseUrl: 'https://foo.com/default/base', @@ -55,7 +55,6 @@ class ListDomainsCommandTest extends TestCase self::assertEquals($expectedOutput, $this->commandTester->getDisplay()); self::assertEquals(ExitCodes::EXIT_SUCCESS, $this->commandTester->getStatusCode()); - $listDomains->shouldHaveBeenCalledOnce(); } public function provideInputsAndOutputs(): iterable From 184ff90b9f7949e82530c700e943d552ec910205 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sat, 22 Oct 2022 13:27:48 +0200 Subject: [PATCH 10/27] Migrated CreateShortUrlCommandTest to use PHPUnit mocks --- .../ShortUrl/CreateShortUrlCommandTest.php | 59 +++++++++---------- 1 file changed, 29 insertions(+), 30 deletions(-) diff --git a/module/CLI/test/Command/ShortUrl/CreateShortUrlCommandTest.php b/module/CLI/test/Command/ShortUrl/CreateShortUrlCommandTest.php index 985a50a4..b1bbad77 100644 --- a/module/CLI/test/Command/ShortUrl/CreateShortUrlCommandTest.php +++ b/module/CLI/test/Command/ShortUrl/CreateShortUrlCommandTest.php @@ -5,9 +5,8 @@ declare(strict_types=1); namespace ShlinkioTest\Shlink\CLI\Command\ShortUrl; use PHPUnit\Framework\Assert; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; -use Prophecy\Argument; -use Prophecy\Prophecy\ObjectProphecy; use Shlinkio\Shlink\CLI\Command\ShortUrl\CreateShortUrlCommand; use Shlinkio\Shlink\CLI\Util\ExitCodes; use Shlinkio\Shlink\Core\Exception\InvalidUrlException; @@ -27,18 +26,17 @@ class CreateShortUrlCommandTest extends TestCase private const DEFAULT_DOMAIN = 'default.com'; private CommandTester $commandTester; - private ObjectProphecy $urlShortener; - private ObjectProphecy $stringifier; + private MockObject $urlShortener; + private MockObject $stringifier; protected function setUp(): void { - $this->urlShortener = $this->prophesize(UrlShortener::class); - $this->stringifier = $this->prophesize(ShortUrlStringifierInterface::class); - $this->stringifier->stringify(Argument::type(ShortUrl::class))->willReturn(''); + $this->urlShortener = $this->createMock(UrlShortener::class); + $this->stringifier = $this->createMock(ShortUrlStringifierInterface::class); $command = new CreateShortUrlCommand( - $this->urlShortener->reveal(), - $this->stringifier->reveal(), + $this->urlShortener, + $this->stringifier, new UrlShortenerOptions(domain: ['hostname' => self::DEFAULT_DOMAIN], defaultShortCodesLength: 5), ); $this->commandTester = $this->testerForCommand($command); @@ -48,8 +46,10 @@ class CreateShortUrlCommandTest extends TestCase public function properShortCodeIsCreatedIfLongUrlIsCorrect(): void { $shortUrl = ShortUrl::createEmpty(); - $urlToShortCode = $this->urlShortener->shorten(Argument::cetera())->willReturn($shortUrl); - $stringify = $this->stringifier->stringify($shortUrl)->willReturn('stringified_short_url'); + $this->urlShortener->expects($this->once())->method('shorten')->withAnyParameters()->willReturn($shortUrl); + $this->stringifier->expects($this->once())->method('stringify')->with($this->equalTo($shortUrl))->willReturn( + 'stringified_short_url', + ); $this->commandTester->execute([ 'longUrl' => 'http://domain.com/foo/bar', @@ -59,16 +59,16 @@ class CreateShortUrlCommandTest extends TestCase self::assertEquals(ExitCodes::EXIT_SUCCESS, $this->commandTester->getStatusCode()); self::assertStringContainsString('stringified_short_url', $output); - $urlToShortCode->shouldHaveBeenCalledOnce(); - $stringify->shouldHaveBeenCalledOnce(); } /** @test */ public function exceptionWhileParsingLongUrlOutputsError(): void { $url = 'http://domain.com/invalid'; - $this->urlShortener->shorten(Argument::cetera())->willThrow(InvalidUrlException::fromUrl($url)) - ->shouldBeCalledOnce(); + $this->urlShortener->expects($this->once())->method('shorten')->withAnyParameters()->willThrowException( + InvalidUrlException::fromUrl($url), + ); + $this->stringifier->method('stringify')->with($this->isInstanceOf(ShortUrl::class))->willReturn(''); $this->commandTester->execute(['longUrl' => $url]); $output = $this->commandTester->getDisplay(); @@ -80,30 +80,32 @@ class CreateShortUrlCommandTest extends TestCase /** @test */ public function providingNonUniqueSlugOutputsError(): void { - $urlToShortCode = $this->urlShortener->shorten(Argument::cetera())->willThrow( + $this->urlShortener->expects($this->once())->method('shorten')->withAnyParameters()->willThrowException( NonUniqueSlugException::fromSlug('my-slug'), ); + $this->stringifier->method('stringify')->with($this->isInstanceOf(ShortUrl::class))->willReturn(''); $this->commandTester->execute(['longUrl' => 'http://domain.com/invalid', '--custom-slug' => 'my-slug']); $output = $this->commandTester->getDisplay(); self::assertEquals(ExitCodes::EXIT_FAILURE, $this->commandTester->getStatusCode()); self::assertStringContainsString('Provided slug "my-slug" is already in use', $output); - $urlToShortCode->shouldHaveBeenCalledOnce(); } /** @test */ public function properlyProcessesProvidedTags(): void { $shortUrl = ShortUrl::createEmpty(); - $urlToShortCode = $this->urlShortener->shorten( - Argument::that(function (ShortUrlCreation $meta) { + $this->urlShortener->expects($this->once())->method('shorten')->with( + $this->callback(function (ShortUrlCreation $meta) { $tags = $meta->getTags(); Assert::assertEquals(['foo', 'bar', 'baz', 'boo', 'zar'], $tags); return true; }), )->willReturn($shortUrl); - $stringify = $this->stringifier->stringify($shortUrl)->willReturn('stringified_short_url'); + $this->stringifier->expects($this->once())->method('stringify')->with($this->equalTo($shortUrl))->willReturn( + 'stringified_short_url', + ); $this->commandTester->execute([ 'longUrl' => 'http://domain.com/foo/bar', @@ -113,8 +115,6 @@ class CreateShortUrlCommandTest extends TestCase self::assertEquals(ExitCodes::EXIT_SUCCESS, $this->commandTester->getStatusCode()); self::assertStringContainsString('stringified_short_url', $output); - $urlToShortCode->shouldHaveBeenCalledOnce(); - $stringify->shouldHaveBeenCalledOnce(); } /** @@ -123,18 +123,18 @@ class CreateShortUrlCommandTest extends TestCase */ public function properlyProcessesProvidedDomain(array $input, ?string $expectedDomain): void { - $shorten = $this->urlShortener->shorten( - Argument::that(function (ShortUrlCreation $meta) use ($expectedDomain) { + $this->urlShortener->expects($this->once())->method('shorten')->with( + $this->callback(function (ShortUrlCreation $meta) use ($expectedDomain) { Assert::assertEquals($expectedDomain, $meta->getDomain()); return true; }), )->willReturn(ShortUrl::createEmpty()); + $this->stringifier->method('stringify')->with($this->isInstanceOf(ShortUrl::class))->willReturn(''); $input['longUrl'] = 'http://domain.com/foo/bar'; $this->commandTester->execute($input); self::assertEquals(ExitCodes::EXIT_SUCCESS, $this->commandTester->getStatusCode()); - $shorten->shouldHaveBeenCalledOnce(); } public function provideDomains(): iterable @@ -152,17 +152,16 @@ class CreateShortUrlCommandTest extends TestCase public function urlValidationHasExpectedValueBasedOnProvidedFlags(array $options, ?bool $expectedValidateUrl): void { $shortUrl = ShortUrl::createEmpty(); - $urlToShortCode = $this->urlShortener->shorten( - Argument::that(function (ShortUrlCreation $meta) use ($expectedValidateUrl) { + $this->urlShortener->expects($this->once())->method('shorten')->with( + $this->callback(function (ShortUrlCreation $meta) use ($expectedValidateUrl) { Assert::assertEquals($expectedValidateUrl, $meta->doValidateUrl()); - return $meta; + return true; }), )->willReturn($shortUrl); + $this->stringifier->method('stringify')->with($this->isInstanceOf(ShortUrl::class))->willReturn(''); $options['longUrl'] = 'http://domain.com/foo/bar'; $this->commandTester->execute($options); - - $urlToShortCode->shouldHaveBeenCalledOnce(); } public function provideFlags(): iterable From acda7f02c64e28564af901874e0a23e979043632 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sat, 22 Oct 2022 13:36:33 +0200 Subject: [PATCH 11/27] Migrated DeleteShortUrlCommandTest to use PHPUnit mocks --- .../ShortUrl/DeleteShortUrlCommandTest.php | 61 ++++++++----------- 1 file changed, 27 insertions(+), 34 deletions(-) diff --git a/module/CLI/test/Command/ShortUrl/DeleteShortUrlCommandTest.php b/module/CLI/test/Command/ShortUrl/DeleteShortUrlCommandTest.php index 92aca306..fce9197a 100644 --- a/module/CLI/test/Command/ShortUrl/DeleteShortUrlCommandTest.php +++ b/module/CLI/test/Command/ShortUrl/DeleteShortUrlCommandTest.php @@ -4,9 +4,8 @@ declare(strict_types=1); namespace ShlinkioTest\Shlink\CLI\Command\ShortUrl; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; -use Prophecy\Argument; -use Prophecy\Prophecy\ObjectProphecy; use Shlinkio\Shlink\CLI\Command\ShortUrl\DeleteShortUrlCommand; use Shlinkio\Shlink\Core\Exception; use Shlinkio\Shlink\Core\ShortUrl\DeleteShortUrlServiceInterface; @@ -14,7 +13,6 @@ use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlIdentifier; use ShlinkioTest\Shlink\CLI\CliTestUtilsTrait; use Symfony\Component\Console\Tester\CommandTester; -use function array_pop; use function sprintf; use const PHP_EOL; @@ -24,23 +22,22 @@ class DeleteShortUrlCommandTest extends TestCase use CliTestUtilsTrait; private CommandTester $commandTester; - private ObjectProphecy $service; + private MockObject $service; protected function setUp(): void { - $this->service = $this->prophesize(DeleteShortUrlServiceInterface::class); - $this->commandTester = $this->testerForCommand(new DeleteShortUrlCommand($this->service->reveal())); + $this->service = $this->createMock(DeleteShortUrlServiceInterface::class); + $this->commandTester = $this->testerForCommand(new DeleteShortUrlCommand($this->service)); } /** @test */ public function successMessageIsPrintedIfUrlIsProperlyDeleted(): void { $shortCode = 'abc123'; - $deleteByShortCode = $this->service->deleteByShortCode( - ShortUrlIdentifier::fromShortCodeAndDomain($shortCode), - false, - )->will(function (): void { - }); + $this->service->expects($this->once())->method('deleteByShortCode')->with( + $this->equalTo(ShortUrlIdentifier::fromShortCodeAndDomain($shortCode)), + $this->isFalse(), + ); $this->commandTester->execute(['shortCode' => $shortCode]); $output = $this->commandTester->getDisplay(); @@ -49,7 +46,6 @@ class DeleteShortUrlCommandTest extends TestCase sprintf('Short URL with short code "%s" successfully deleted.', $shortCode), $output, ); - $deleteByShortCode->shouldHaveBeenCalledOnce(); } /** @test */ @@ -57,15 +53,15 @@ class DeleteShortUrlCommandTest extends TestCase { $shortCode = 'abc123'; $identifier = ShortUrlIdentifier::fromShortCodeAndDomain($shortCode); - $deleteByShortCode = $this->service->deleteByShortCode($identifier, false)->willThrow( - Exception\ShortUrlNotFoundException::fromNotFound($identifier), - ); + $this->service->expects($this->once())->method('deleteByShortCode')->with( + $this->equalTo($identifier), + $this->isFalse(), + )->willThrowException(Exception\ShortUrlNotFoundException::fromNotFound($identifier)); $this->commandTester->execute(['shortCode' => $shortCode]); $output = $this->commandTester->getDisplay(); self::assertStringContainsString(sprintf('No URL found with short code "%s"', $shortCode), $output); - $deleteByShortCode->shouldHaveBeenCalledOnce(); } /** @@ -79,18 +75,17 @@ class DeleteShortUrlCommandTest extends TestCase ): void { $shortCode = 'abc123'; $identifier = ShortUrlIdentifier::fromShortCodeAndDomain($shortCode); - $deleteByShortCode = $this->service->deleteByShortCode($identifier, Argument::type('bool'))->will( - function (array $args) use ($shortCode): void { - $ignoreThreshold = array_pop($args); - - if (!$ignoreThreshold) { - throw Exception\DeleteShortUrlException::fromVisitsThreshold( - 10, - ShortUrlIdentifier::fromShortCodeAndDomain($shortCode), - ); - } - }, - ); + $this->service->expects($this->exactly($expectedDeleteCalls))->method('deleteByShortCode')->with( + $this->equalTo($identifier), + $this->isType('bool'), + )->willReturnCallback(function ($_, bool $ignoreThreshold) use ($shortCode): void { + if (!$ignoreThreshold) { + throw Exception\DeleteShortUrlException::fromVisitsThreshold( + 10, + ShortUrlIdentifier::fromShortCodeAndDomain($shortCode), + ); + } + }); $this->commandTester->setInputs($retryAnswer); $this->commandTester->execute(['shortCode' => $shortCode]); @@ -101,7 +96,6 @@ class DeleteShortUrlCommandTest extends TestCase $shortCode, ), $output); self::assertStringContainsString($expectedMessage, $output); - $deleteByShortCode->shouldHaveBeenCalledTimes($expectedDeleteCalls); } public function provideRetryDeleteAnswers(): iterable @@ -115,10 +109,10 @@ class DeleteShortUrlCommandTest extends TestCase public function deleteIsNotRetriedWhenThresholdIsReachedAndQuestionIsDeclined(): void { $shortCode = 'abc123'; - $deleteByShortCode = $this->service->deleteByShortCode( - ShortUrlIdentifier::fromShortCodeAndDomain($shortCode), - false, - )->willThrow(Exception\DeleteShortUrlException::fromVisitsThreshold( + $this->service->expects($this->once())->method('deleteByShortCode')->with( + $this->equalTo(ShortUrlIdentifier::fromShortCodeAndDomain($shortCode)), + $this->isFalse(), + )->willThrowException(Exception\DeleteShortUrlException::fromVisitsThreshold( 10, ShortUrlIdentifier::fromShortCodeAndDomain($shortCode), )); @@ -132,6 +126,5 @@ class DeleteShortUrlCommandTest extends TestCase $shortCode, ), $output); self::assertStringContainsString('Short URL was not deleted.', $output); - $deleteByShortCode->shouldHaveBeenCalledOnce(); } } From 8b675f55cc0edb867d852a49ea210c212982cee3 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sat, 22 Oct 2022 13:38:46 +0200 Subject: [PATCH 12/27] Migrated GetShortUrlVisitsCommandTest to use PHPUnit mocks --- .../ShortUrl/GetShortUrlVisitsCommandTest.php | 46 ++++++++----------- 1 file changed, 19 insertions(+), 27 deletions(-) diff --git a/module/CLI/test/Command/ShortUrl/GetShortUrlVisitsCommandTest.php b/module/CLI/test/Command/ShortUrl/GetShortUrlVisitsCommandTest.php index 00fca968..f271e26b 100644 --- a/module/CLI/test/Command/ShortUrl/GetShortUrlVisitsCommandTest.php +++ b/module/CLI/test/Command/ShortUrl/GetShortUrlVisitsCommandTest.php @@ -6,9 +6,8 @@ namespace ShlinkioTest\Shlink\CLI\Command\ShortUrl; use Cake\Chronos\Chronos; use Pagerfanta\Adapter\ArrayAdapter; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; -use Prophecy\Argument; -use Prophecy\Prophecy\ObjectProphecy; use Shlinkio\Shlink\CLI\Command\ShortUrl\GetShortUrlVisitsCommand; use Shlinkio\Shlink\Common\Paginator\Paginator; use Shlinkio\Shlink\Common\Util\DateRange; @@ -31,12 +30,12 @@ class GetShortUrlVisitsCommandTest extends TestCase use CliTestUtilsTrait; private CommandTester $commandTester; - private ObjectProphecy $visitsHelper; + private MockObject $visitsHelper; protected function setUp(): void { - $this->visitsHelper = $this->prophesize(VisitsStatsHelperInterface::class); - $command = new GetShortUrlVisitsCommand($this->visitsHelper->reveal()); + $this->visitsHelper = $this->createMock(VisitsStatsHelperInterface::class); + $command = new GetShortUrlVisitsCommand($this->visitsHelper); $this->commandTester = $this->testerForCommand($command); } @@ -44,12 +43,10 @@ class GetShortUrlVisitsCommandTest extends TestCase public function noDateFlagsTriesToListWithoutDateRange(): void { $shortCode = 'abc123'; - $this->visitsHelper->visitsForShortUrl( - ShortUrlIdentifier::fromShortCodeAndDomain($shortCode), - new VisitsParams(DateRange::allTime()), - ) - ->willReturn(new Paginator(new ArrayAdapter([]))) - ->shouldBeCalledOnce(); + $this->visitsHelper->expects($this->once())->method('visitsForShortUrl')->with( + $this->equalTo(ShortUrlIdentifier::fromShortCodeAndDomain($shortCode)), + $this->equalTo(new VisitsParams(DateRange::allTime())), + )->willReturn(new Paginator(new ArrayAdapter([]))); $this->commandTester->execute(['shortCode' => $shortCode]); } @@ -60,12 +57,10 @@ class GetShortUrlVisitsCommandTest extends TestCase $shortCode = 'abc123'; $startDate = '2016-01-01'; $endDate = '2016-02-01'; - $this->visitsHelper->visitsForShortUrl( - ShortUrlIdentifier::fromShortCodeAndDomain($shortCode), - new VisitsParams(buildDateRange(Chronos::parse($startDate), Chronos::parse($endDate))), - ) - ->willReturn(new Paginator(new ArrayAdapter([]))) - ->shouldBeCalledOnce(); + $this->visitsHelper->expects($this->once())->method('visitsForShortUrl')->with( + $this->equalTo(ShortUrlIdentifier::fromShortCodeAndDomain($shortCode)), + $this->equalTo(new VisitsParams(buildDateRange(Chronos::parse($startDate), Chronos::parse($endDate)))), + )->willReturn(new Paginator(new ArrayAdapter([]))); $this->commandTester->execute([ 'shortCode' => $shortCode, @@ -79,9 +74,9 @@ class GetShortUrlVisitsCommandTest extends TestCase { $shortCode = 'abc123'; $startDate = 'foo'; - $info = $this->visitsHelper->visitsForShortUrl( - ShortUrlIdentifier::fromShortCodeAndDomain($shortCode), - new VisitsParams(DateRange::allTime()), + $this->visitsHelper->expects($this->once())->method('visitsForShortUrl')->with( + $this->equalTo(ShortUrlIdentifier::fromShortCodeAndDomain($shortCode)), + $this->equalTo(new VisitsParams(DateRange::allTime())), )->willReturn(new Paginator(new ArrayAdapter([]))); $this->commandTester->execute([ @@ -90,7 +85,6 @@ class GetShortUrlVisitsCommandTest extends TestCase ]); $output = $this->commandTester->getDisplay(); - $info->shouldHaveBeenCalledOnce(); self::assertStringContainsString( sprintf('Ignored provided "start-date" since its value "%s" is not a valid date', $startDate), $output, @@ -104,12 +98,10 @@ class GetShortUrlVisitsCommandTest extends TestCase VisitLocation::fromGeolocation(new Location('', 'Spain', '', 'Madrid', 0, 0, '')), ); $shortCode = 'abc123'; - $this->visitsHelper->visitsForShortUrl( - ShortUrlIdentifier::fromShortCodeAndDomain($shortCode), - Argument::any(), - )->willReturn( - new Paginator(new ArrayAdapter([$visit])), - )->shouldBeCalledOnce(); + $this->visitsHelper->expects($this->once())->method('visitsForShortUrl')->with( + $this->equalTo(ShortUrlIdentifier::fromShortCodeAndDomain($shortCode)), + $this->anything(), + )->willReturn(new Paginator(new ArrayAdapter([$visit]))); $this->commandTester->execute(['shortCode' => $shortCode]); $output = $this->commandTester->getDisplay(); From 4872bd3a92682ed9ea5905809dc41cf4a3e17850 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sat, 22 Oct 2022 13:42:46 +0200 Subject: [PATCH 13/27] Migrated ListShortUrlsCommandTest to use PHPUnit mocks --- .../ShortUrl/ListShortUrlsCommandTest.php | 98 +++++++++---------- 1 file changed, 48 insertions(+), 50 deletions(-) diff --git a/module/CLI/test/Command/ShortUrl/ListShortUrlsCommandTest.php b/module/CLI/test/Command/ShortUrl/ListShortUrlsCommandTest.php index 5659059a..dfd1a028 100644 --- a/module/CLI/test/Command/ShortUrl/ListShortUrlsCommandTest.php +++ b/module/CLI/test/Command/ShortUrl/ListShortUrlsCommandTest.php @@ -6,9 +6,8 @@ namespace ShlinkioTest\Shlink\CLI\Command\ShortUrl; use Cake\Chronos\Chronos; use Pagerfanta\Adapter\ArrayAdapter; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; -use Prophecy\Argument; -use Prophecy\Prophecy\ObjectProphecy; use Shlinkio\Shlink\CLI\Command\ShortUrl\ListShortUrlsCommand; use Shlinkio\Shlink\Common\Paginator\Paginator; use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl; @@ -31,12 +30,12 @@ class ListShortUrlsCommandTest extends TestCase use CliTestUtilsTrait; private CommandTester $commandTester; - private ObjectProphecy $shortUrlService; + private MockObject $shortUrlService; protected function setUp(): void { - $this->shortUrlService = $this->prophesize(ShortUrlServiceInterface::class); - $command = new ListShortUrlsCommand($this->shortUrlService->reveal(), new ShortUrlDataTransformer( + $this->shortUrlService = $this->createMock(ShortUrlServiceInterface::class); + $command = new ListShortUrlsCommand($this->shortUrlService, new ShortUrlDataTransformer( new ShortUrlStringifier([]), )); $this->commandTester = $this->testerForCommand($command); @@ -51,9 +50,8 @@ class ListShortUrlsCommandTest extends TestCase $data[] = ShortUrl::withLongUrl('url_' . $i); } - $this->shortUrlService->listShortUrls(Argument::cetera()) - ->will(fn () => new Paginator(new ArrayAdapter($data))) - ->shouldBeCalledTimes(3); + $this->shortUrlService->expects($this->exactly(3))->method('listShortUrls')->withAnyParameters() + ->willReturnCallback(fn () => new Paginator(new ArrayAdapter($data))); $this->commandTester->setInputs(['y', 'y', 'n']); $this->commandTester->execute([]); @@ -74,9 +72,9 @@ class ListShortUrlsCommandTest extends TestCase $data[] = ShortUrl::withLongUrl('url_' . $i); } - $this->shortUrlService->listShortUrls(ShortUrlsParams::emptyInstance()) - ->willReturn(new Paginator(new ArrayAdapter($data))) - ->shouldBeCalledOnce(); + $this->shortUrlService->expects($this->once())->method('listShortUrls')->with( + $this->equalTo(ShortUrlsParams::emptyInstance()) + )->willReturn(new Paginator(new ArrayAdapter($data))); $this->commandTester->setInputs(['n']); $this->commandTester->execute([]); @@ -95,9 +93,9 @@ class ListShortUrlsCommandTest extends TestCase public function passingPageWillMakeListStartOnThatPage(): void { $page = 5; - $this->shortUrlService->listShortUrls(ShortUrlsParams::fromRawData(['page' => $page])) - ->willReturn(new Paginator(new ArrayAdapter([]))) - ->shouldBeCalledOnce(); + $this->shortUrlService->expects($this->once())->method('listShortUrls')->with( + $this->equalTo(ShortUrlsParams::fromRawData(['page' => $page])), + )->willReturn(new Paginator(new ArrayAdapter([]))); $this->commandTester->setInputs(['y']); $this->commandTester->execute(['--page' => $page]); @@ -113,15 +111,15 @@ class ListShortUrlsCommandTest extends TestCase array $notExpectedContents, ApiKey $apiKey, ): void { - $this->shortUrlService->listShortUrls(ShortUrlsParams::emptyInstance()) - ->willReturn(new Paginator(new ArrayAdapter([ - ShortUrl::fromMeta(ShortUrlCreation::fromRawData([ - 'longUrl' => 'foo.com', - 'tags' => ['foo', 'bar', 'baz'], - 'apiKey' => $apiKey, - ])), - ]))) - ->shouldBeCalledOnce(); + $this->shortUrlService->expects($this->once())->method('listShortUrls')->with( + $this->equalTo(ShortUrlsParams::emptyInstance()), + )->willReturn(new Paginator(new ArrayAdapter([ + ShortUrl::fromMeta(ShortUrlCreation::fromRawData([ + 'longUrl' => 'foo.com', + 'tags' => ['foo', 'bar', 'baz'], + 'apiKey' => $apiKey, + ])), + ]))); $this->commandTester->setInputs(['y']); $this->commandTester->execute($input); @@ -189,19 +187,19 @@ class ListShortUrlsCommandTest extends TestCase ?string $startDate = null, ?string $endDate = null, ): void { - $listShortUrls = $this->shortUrlService->listShortUrls(ShortUrlsParams::fromRawData([ - 'page' => $page, - 'searchTerm' => $searchTerm, - 'tags' => $tags, - 'tagsMode' => $tagsMode, - 'startDate' => $startDate !== null ? Chronos::parse($startDate)->toAtomString() : null, - 'endDate' => $endDate !== null ? Chronos::parse($endDate)->toAtomString() : null, - ]))->willReturn(new Paginator(new ArrayAdapter([]))); + $this->shortUrlService->expects($this->once())->method('listShortUrls')->with( + $this->equalTo(ShortUrlsParams::fromRawData([ + 'page' => $page, + 'searchTerm' => $searchTerm, + 'tags' => $tags, + 'tagsMode' => $tagsMode, + 'startDate' => $startDate !== null ? Chronos::parse($startDate)->toAtomString() : null, + 'endDate' => $endDate !== null ? Chronos::parse($endDate)->toAtomString() : null, + ])), + )->willReturn(new Paginator(new ArrayAdapter([]))); $this->commandTester->setInputs(['n']); $this->commandTester->execute($commandArgs); - - $listShortUrls->shouldHaveBeenCalledOnce(); } public function provideArgs(): iterable @@ -251,14 +249,14 @@ class ListShortUrlsCommandTest extends TestCase */ public function orderByIsProperlyComputed(array $commandArgs, ?string $expectedOrderBy): void { - $listShortUrls = $this->shortUrlService->listShortUrls(ShortUrlsParams::fromRawData([ - 'orderBy' => $expectedOrderBy, - ]))->willReturn(new Paginator(new ArrayAdapter([]))); + $this->shortUrlService->expects($this->once())->method('listShortUrls')->with( + $this->equalTo(ShortUrlsParams::fromRawData([ + 'orderBy' => $expectedOrderBy, + ])), + )->willReturn(new Paginator(new ArrayAdapter([]))); $this->commandTester->setInputs(['n']); $this->commandTester->execute($commandArgs); - - $listShortUrls->shouldHaveBeenCalledOnce(); } public function provideOrderBy(): iterable @@ -273,19 +271,19 @@ class ListShortUrlsCommandTest extends TestCase /** @test */ public function requestingAllElementsWillSetItemsPerPage(): void { - $listShortUrls = $this->shortUrlService->listShortUrls(ShortUrlsParams::fromRawData([ - 'page' => 1, - 'searchTerm' => null, - 'tags' => [], - 'tagsMode' => TagsMode::ANY->value, - 'startDate' => null, - 'endDate' => null, - 'orderBy' => null, - 'itemsPerPage' => Paginator::ALL_ITEMS, - ]))->willReturn(new Paginator(new ArrayAdapter([]))); + $this->shortUrlService->expects($this->once())->method('listShortUrls')->with( + $this->equalTo(ShortUrlsParams::fromRawData([ + 'page' => 1, + 'searchTerm' => null, + 'tags' => [], + 'tagsMode' => TagsMode::ANY->value, + 'startDate' => null, + 'endDate' => null, + 'orderBy' => null, + 'itemsPerPage' => Paginator::ALL_ITEMS, + ])), + )->willReturn(new Paginator(new ArrayAdapter([]))); $this->commandTester->execute(['--all' => true]); - - $listShortUrls->shouldHaveBeenCalledOnce(); } } From 41e903cf267f1666a2a4705333f439b3b3595c3f Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sat, 22 Oct 2022 13:44:10 +0200 Subject: [PATCH 14/27] Migrated ResolveUrlCommandTest to use PHPUnit mocks --- .../ShortUrl/ResolveUrlCommandTest.php | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/module/CLI/test/Command/ShortUrl/ResolveUrlCommandTest.php b/module/CLI/test/Command/ShortUrl/ResolveUrlCommandTest.php index 6050f736..3b063ab4 100644 --- a/module/CLI/test/Command/ShortUrl/ResolveUrlCommandTest.php +++ b/module/CLI/test/Command/ShortUrl/ResolveUrlCommandTest.php @@ -4,8 +4,8 @@ declare(strict_types=1); namespace ShlinkioTest\Shlink\CLI\Command\ShortUrl; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; -use Prophecy\Prophecy\ObjectProphecy; use Shlinkio\Shlink\CLI\Command\ShortUrl\ResolveUrlCommand; use Shlinkio\Shlink\Core\Exception\ShortUrlNotFoundException; use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl; @@ -23,12 +23,12 @@ class ResolveUrlCommandTest extends TestCase use CliTestUtilsTrait; private CommandTester $commandTester; - private ObjectProphecy $urlResolver; + private MockObject $urlResolver; protected function setUp(): void { - $this->urlResolver = $this->prophesize(ShortUrlResolverInterface::class); - $this->commandTester = $this->testerForCommand(new ResolveUrlCommand($this->urlResolver->reveal())); + $this->urlResolver = $this->createMock(ShortUrlResolverInterface::class); + $this->commandTester = $this->testerForCommand(new ResolveUrlCommand($this->urlResolver)); } /** @test */ @@ -37,9 +37,9 @@ class ResolveUrlCommandTest extends TestCase $shortCode = 'abc123'; $expectedUrl = 'http://domain.com/foo/bar'; $shortUrl = ShortUrl::withLongUrl($expectedUrl); - $this->urlResolver->resolveShortUrl(ShortUrlIdentifier::fromShortCodeAndDomain($shortCode))->willReturn( - $shortUrl, - )->shouldBeCalledOnce(); + $this->urlResolver->expects($this->once())->method('resolveShortUrl')->with( + $this->equalTo(ShortUrlIdentifier::fromShortCodeAndDomain($shortCode)), + )->willReturn($shortUrl); $this->commandTester->execute(['shortCode' => $shortCode]); $output = $this->commandTester->getDisplay(); @@ -52,9 +52,9 @@ class ResolveUrlCommandTest extends TestCase $identifier = ShortUrlIdentifier::fromShortCodeAndDomain('abc123'); $shortCode = $identifier->shortCode; - $this->urlResolver->resolveShortUrl($identifier) - ->willThrow(ShortUrlNotFoundException::fromNotFound($identifier)) - ->shouldBeCalledOnce(); + $this->urlResolver->expects($this->once())->method('resolveShortUrl')->with( + $this->equalTo($identifier) + )->willThrowException(ShortUrlNotFoundException::fromNotFound($identifier)); $this->commandTester->execute(['shortCode' => $shortCode]); $output = $this->commandTester->getDisplay(); From 2d168565820b5d925e23dc4d21e5b16a780328d0 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sat, 22 Oct 2022 13:45:11 +0200 Subject: [PATCH 15/27] Migrated DeleteTagsCommandTest to use PHPUnit mocks --- .../CLI/test/Command/Tag/DeleteTagsCommandTest.php | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/module/CLI/test/Command/Tag/DeleteTagsCommandTest.php b/module/CLI/test/Command/Tag/DeleteTagsCommandTest.php index b03bf1ee..fbaa248f 100644 --- a/module/CLI/test/Command/Tag/DeleteTagsCommandTest.php +++ b/module/CLI/test/Command/Tag/DeleteTagsCommandTest.php @@ -4,8 +4,8 @@ declare(strict_types=1); namespace ShlinkioTest\Shlink\CLI\Command\Tag; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; -use Prophecy\Prophecy\ObjectProphecy; use Shlinkio\Shlink\CLI\Command\Tag\DeleteTagsCommand; use Shlinkio\Shlink\Core\Tag\TagServiceInterface; use ShlinkioTest\Shlink\CLI\CliTestUtilsTrait; @@ -16,12 +16,12 @@ class DeleteTagsCommandTest extends TestCase use CliTestUtilsTrait; private CommandTester $commandTester; - private ObjectProphecy $tagService; + private MockObject $tagService; protected function setUp(): void { - $this->tagService = $this->prophesize(TagServiceInterface::class); - $this->commandTester = $this->testerForCommand(new DeleteTagsCommand($this->tagService->reveal())); + $this->tagService = $this->createMock(TagServiceInterface::class); + $this->commandTester = $this->testerForCommand(new DeleteTagsCommand($this->tagService)); } /** @test */ @@ -37,8 +37,7 @@ class DeleteTagsCommandTest extends TestCase public function serviceIsInvokedOnSuccess(): void { $tagNames = ['foo', 'bar']; - $deleteTags = $this->tagService->deleteTags($tagNames)->will(function (): void { - }); + $this->tagService->expects($this->once())->method('deleteTags')->with($this->equalTo($tagNames)); $this->commandTester->execute([ '--name' => $tagNames, @@ -46,6 +45,5 @@ class DeleteTagsCommandTest extends TestCase $output = $this->commandTester->getDisplay(); self::assertStringContainsString('Tags properly deleted', $output); - $deleteTags->shouldHaveBeenCalled(); } } From 8c6f97c4e26a9c6027348c95721ac0ddc7bcce8f Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sat, 22 Oct 2022 13:47:28 +0200 Subject: [PATCH 16/27] Migrated GetTagVisitsCommandTest to use PHPUnit mocks --- .../Command/Tag/GetTagVisitsCommandTest.php | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/module/CLI/test/Command/Tag/GetTagVisitsCommandTest.php b/module/CLI/test/Command/Tag/GetTagVisitsCommandTest.php index dedbbf83..f2832541 100644 --- a/module/CLI/test/Command/Tag/GetTagVisitsCommandTest.php +++ b/module/CLI/test/Command/Tag/GetTagVisitsCommandTest.php @@ -5,9 +5,8 @@ declare(strict_types=1); namespace ShlinkioTest\Shlink\CLI\Command\Tag; use Pagerfanta\Adapter\ArrayAdapter; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; -use Prophecy\Argument; -use Prophecy\Prophecy\ObjectProphecy; use Shlinkio\Shlink\CLI\Command\Tag\GetTagVisitsCommand; use Shlinkio\Shlink\Common\Paginator\Paginator; use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl; @@ -25,16 +24,16 @@ class GetTagVisitsCommandTest extends TestCase use CliTestUtilsTrait; private CommandTester $commandTester; - private ObjectProphecy $visitsHelper; - private ObjectProphecy $stringifier; + private MockObject $visitsHelper; + private MockObject $stringifier; protected function setUp(): void { - $this->visitsHelper = $this->prophesize(VisitsStatsHelperInterface::class); - $this->stringifier = $this->prophesize(ShortUrlStringifierInterface::class); + $this->visitsHelper = $this->createMock(VisitsStatsHelperInterface::class); + $this->stringifier = $this->createMock(ShortUrlStringifierInterface::class); $this->commandTester = $this->testerForCommand( - new GetTagVisitsCommand($this->visitsHelper->reveal(), $this->stringifier->reveal()), + new GetTagVisitsCommand($this->visitsHelper, $this->stringifier), ); } @@ -46,10 +45,13 @@ class GetTagVisitsCommandTest extends TestCase VisitLocation::fromGeolocation(new Location('', 'Spain', '', 'Madrid', 0, 0, '')), ); $tag = 'abc123'; - $getVisits = $this->visitsHelper->visitsForTag($tag, Argument::any())->willReturn( - new Paginator(new ArrayAdapter([$visit])), + $this->visitsHelper->expects($this->once())->method('visitsForTag')->with( + $this->equalTo($tag), + $this->anything(), + )->willReturn(new Paginator(new ArrayAdapter([$visit]))); + $this->stringifier->expects($this->once())->method('stringify')->with($this->equalTo($shortUrl))->willReturn( + 'the_short_url', ); - $stringify = $this->stringifier->stringify($shortUrl)->willReturn('the_short_url'); $this->commandTester->execute(['tag' => $tag]); $output = $this->commandTester->getDisplay(); @@ -65,7 +67,5 @@ class GetTagVisitsCommandTest extends TestCase OUTPUT, $output, ); - $getVisits->shouldHaveBeenCalledOnce(); - $stringify->shouldHaveBeenCalledOnce(); } } From 085510406842c6031898c72d6246dcda90784a87 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sat, 22 Oct 2022 13:49:11 +0200 Subject: [PATCH 17/27] Migrated ListTagsCommandTest to use PHPUnit mocks --- .../test/Command/Tag/ListTagsCommandTest.php | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/module/CLI/test/Command/Tag/ListTagsCommandTest.php b/module/CLI/test/Command/Tag/ListTagsCommandTest.php index 58ae1ef1..e3802a1a 100644 --- a/module/CLI/test/Command/Tag/ListTagsCommandTest.php +++ b/module/CLI/test/Command/Tag/ListTagsCommandTest.php @@ -5,9 +5,8 @@ declare(strict_types=1); namespace ShlinkioTest\Shlink\CLI\Command\Tag; use Pagerfanta\Adapter\ArrayAdapter; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; -use Prophecy\Argument; -use Prophecy\Prophecy\ObjectProphecy; use Shlinkio\Shlink\CLI\Command\Tag\ListTagsCommand; use Shlinkio\Shlink\Common\Paginator\Paginator; use Shlinkio\Shlink\Core\Tag\Model\TagInfo; @@ -20,33 +19,36 @@ class ListTagsCommandTest extends TestCase use CliTestUtilsTrait; private CommandTester $commandTester; - private ObjectProphecy $tagService; + private MockObject $tagService; protected function setUp(): void { - $this->tagService = $this->prophesize(TagServiceInterface::class); - $this->commandTester = $this->testerForCommand(new ListTagsCommand($this->tagService->reveal())); + $this->tagService = $this->createMock(TagServiceInterface::class); + $this->commandTester = $this->testerForCommand(new ListTagsCommand($this->tagService)); } /** @test */ public function noTagsPrintsEmptyMessage(): void { - $tagsInfo = $this->tagService->tagsInfo(Argument::any())->willReturn(new Paginator(new ArrayAdapter([]))); + $this->tagService->expects($this->once())->method('tagsInfo')->withAnyParameters()->willReturn( + new Paginator(new ArrayAdapter([])), + ); $this->commandTester->execute([]); $output = $this->commandTester->getDisplay(); self::assertStringContainsString('No tags found', $output); - $tagsInfo->shouldHaveBeenCalled(); } /** @test */ public function listOfTagsIsPrinted(): void { - $tagsInfo = $this->tagService->tagsInfo(Argument::any())->willReturn(new Paginator(new ArrayAdapter([ - new TagInfo('foo', 10, 2), - new TagInfo('bar', 7, 32), - ]))); + $this->tagService->expects($this->once())->method('tagsInfo')->withAnyParameters()->willReturn( + new Paginator(new ArrayAdapter([ + new TagInfo('foo', 10, 2), + new TagInfo('bar', 7, 32), + ])), + ); $this->commandTester->execute([]); $output = $this->commandTester->getDisplay(); @@ -63,6 +65,5 @@ class ListTagsCommandTest extends TestCase OUTPUT, $output, ); - $tagsInfo->shouldHaveBeenCalled(); } } From 59de5a5f551a6ae41195c2adab3698c05c003b17 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sat, 22 Oct 2022 13:53:45 +0200 Subject: [PATCH 18/27] Migrated RenameTagCommandTest to use PHPUnit mocks --- .../test/Command/Tag/RenameTagCommandTest.php | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/module/CLI/test/Command/Tag/RenameTagCommandTest.php b/module/CLI/test/Command/Tag/RenameTagCommandTest.php index 9de8d154..a3429e40 100644 --- a/module/CLI/test/Command/Tag/RenameTagCommandTest.php +++ b/module/CLI/test/Command/Tag/RenameTagCommandTest.php @@ -4,8 +4,8 @@ declare(strict_types=1); namespace ShlinkioTest\Shlink\CLI\Command\Tag; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; -use Prophecy\Prophecy\ObjectProphecy; use Shlinkio\Shlink\CLI\Command\Tag\RenameTagCommand; use Shlinkio\Shlink\Core\Exception\TagNotFoundException; use Shlinkio\Shlink\Core\Tag\Entity\Tag; @@ -19,12 +19,12 @@ class RenameTagCommandTest extends TestCase use CliTestUtilsTrait; private CommandTester $commandTester; - private ObjectProphecy $tagService; + private MockObject $tagService; protected function setUp(): void { - $this->tagService = $this->prophesize(TagServiceInterface::class); - $this->commandTester = $this->testerForCommand(new RenameTagCommand($this->tagService->reveal())); + $this->tagService = $this->createMock(TagServiceInterface::class); + $this->commandTester = $this->testerForCommand(new RenameTagCommand($this->tagService)); } /** @test */ @@ -32,9 +32,9 @@ class RenameTagCommandTest extends TestCase { $oldName = 'foo'; $newName = 'bar'; - $renameTag = $this->tagService->renameTag(TagRenaming::fromNames($oldName, $newName))->willThrow( - TagNotFoundException::fromTag('foo'), - ); + $this->tagService->expects($this->once())->method('renameTag')->with( + $this->equalTo(TagRenaming::fromNames($oldName, $newName)), + )->willThrowException(TagNotFoundException::fromTag('foo')); $this->commandTester->execute([ 'oldName' => $oldName, @@ -43,7 +43,6 @@ class RenameTagCommandTest extends TestCase $output = $this->commandTester->getDisplay(); self::assertStringContainsString('Tag with name "foo" could not be found', $output); - $renameTag->shouldHaveBeenCalled(); } /** @test */ @@ -51,9 +50,9 @@ class RenameTagCommandTest extends TestCase { $oldName = 'foo'; $newName = 'bar'; - $renameTag = $this->tagService->renameTag(TagRenaming::fromNames($oldName, $newName))->willReturn( - new Tag($newName), - ); + $this->tagService->expects($this->once())->method('renameTag')->with( + $this->equalTo(TagRenaming::fromNames($oldName, $newName)), + )->willReturn(new Tag($newName)); $this->commandTester->execute([ 'oldName' => $oldName, @@ -62,6 +61,5 @@ class RenameTagCommandTest extends TestCase $output = $this->commandTester->getDisplay(); self::assertStringContainsString('Tag properly renamed', $output); - $renameTag->shouldHaveBeenCalled(); } } From 5d367da626e2d3c8b254801ce8913423c5f128d1 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sat, 22 Oct 2022 14:02:38 +0200 Subject: [PATCH 19/27] Migrated DownloadGeoLiteDbCommandTest to use PHPUnit mocks --- .../Visit/DownloadGeoLiteDbCommandTest.php | 25 ++++++++----------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/module/CLI/test/Command/Visit/DownloadGeoLiteDbCommandTest.php b/module/CLI/test/Command/Visit/DownloadGeoLiteDbCommandTest.php index 93405799..b5197dde 100644 --- a/module/CLI/test/Command/Visit/DownloadGeoLiteDbCommandTest.php +++ b/module/CLI/test/Command/Visit/DownloadGeoLiteDbCommandTest.php @@ -4,9 +4,8 @@ declare(strict_types=1); namespace ShlinkioTest\Shlink\CLI\Command\Visit; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; -use Prophecy\Argument; -use Prophecy\Prophecy\ObjectProphecy; use Shlinkio\Shlink\CLI\Command\Visit\DownloadGeoLiteDbCommand; use Shlinkio\Shlink\CLI\Exception\GeolocationDbUpdateFailedException; use Shlinkio\Shlink\CLI\GeoLite\GeolocationDbUpdaterInterface; @@ -22,12 +21,12 @@ class DownloadGeoLiteDbCommandTest extends TestCase use CliTestUtilsTrait; private CommandTester $commandTester; - private ObjectProphecy $dbUpdater; + private MockObject $dbUpdater; protected function setUp(): void { - $this->dbUpdater = $this->prophesize(GeolocationDbUpdaterInterface::class); - $this->commandTester = $this->testerForCommand(new DownloadGeoLiteDbCommand($this->dbUpdater->reveal())); + $this->dbUpdater = $this->createMock(GeolocationDbUpdaterInterface::class); + $this->commandTester = $this->testerForCommand(new DownloadGeoLiteDbCommand($this->dbUpdater)); } /** @@ -39,10 +38,8 @@ class DownloadGeoLiteDbCommandTest extends TestCase string $expectedMessage, int $expectedExitCode, ): void { - $checkDbUpdate = $this->dbUpdater->checkDbUpdate(Argument::cetera())->will( - function (array $args) use ($olderDbExists): void { - [$beforeDownload, $handleProgress] = $args; - + $this->dbUpdater->expects($this->once())->method('checkDbUpdate')->withAnyParameters()->willReturnCallback( + function (callable $beforeDownload, callable $handleProgress) use ($olderDbExists): void { $beforeDownload($olderDbExists); $handleProgress(100, 50); @@ -62,7 +59,6 @@ class DownloadGeoLiteDbCommandTest extends TestCase ); self::assertStringContainsString($expectedMessage, $output); self::assertSame($expectedExitCode, $exitCode); - $checkDbUpdate->shouldHaveBeenCalledOnce(); } public function provideFailureParams(): iterable @@ -85,7 +81,9 @@ class DownloadGeoLiteDbCommandTest extends TestCase */ public function printsExpectedMessageWhenNoErrorOccurs(callable $checkUpdateBehavior, string $expectedMessage): void { - $checkDbUpdate = $this->dbUpdater->checkDbUpdate(Argument::cetera())->will($checkUpdateBehavior); + $this->dbUpdater->expects($this->once())->method('checkDbUpdate')->withAnyParameters()->willReturnCallback( + $checkUpdateBehavior, + ); $this->commandTester->execute([]); $output = $this->commandTester->getDisplay(); @@ -93,16 +91,13 @@ class DownloadGeoLiteDbCommandTest extends TestCase self::assertStringContainsString($expectedMessage, $output); self::assertSame(ExitCodes::EXIT_SUCCESS, $exitCode); - $checkDbUpdate->shouldHaveBeenCalledOnce(); } public function provideSuccessParams(): iterable { yield 'up to date db' => [fn () => GeolocationResult::CHECK_SKIPPED, '[INFO] GeoLite2 db file is up to date.']; - yield 'outdated db' => [function (array $args): GeolocationResult { - [$beforeDownload] = $args; + yield 'outdated db' => [function (callable $beforeDownload): GeolocationResult { $beforeDownload(true); - return GeolocationResult::DB_CREATED; }, '[OK] GeoLite2 db file properly downloaded.']; } From 82e04800aadb6ce85972d573f355bc16e8e9ba33 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sat, 22 Oct 2022 14:06:00 +0200 Subject: [PATCH 20/27] Migrated GetNonOrphanVisitsCommandTest to use PHPUnit mocks --- .../Visit/GetNonOrphanVisitsCommandTest.php | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/module/CLI/test/Command/Visit/GetNonOrphanVisitsCommandTest.php b/module/CLI/test/Command/Visit/GetNonOrphanVisitsCommandTest.php index 6f83b8b5..23f20da9 100644 --- a/module/CLI/test/Command/Visit/GetNonOrphanVisitsCommandTest.php +++ b/module/CLI/test/Command/Visit/GetNonOrphanVisitsCommandTest.php @@ -5,9 +5,8 @@ declare(strict_types=1); namespace ShlinkioTest\Shlink\CLI\Command\Visit; use Pagerfanta\Adapter\ArrayAdapter; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; -use Prophecy\Argument; -use Prophecy\Prophecy\ObjectProphecy; use Shlinkio\Shlink\CLI\Command\Visit\GetNonOrphanVisitsCommand; use Shlinkio\Shlink\Common\Paginator\Paginator; use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl; @@ -25,16 +24,16 @@ class GetNonOrphanVisitsCommandTest extends TestCase use CliTestUtilsTrait; private CommandTester $commandTester; - private ObjectProphecy $visitsHelper; - private ObjectProphecy $stringifier; + private MockObject $visitsHelper; + private MockObject $stringifier; protected function setUp(): void { - $this->visitsHelper = $this->prophesize(VisitsStatsHelperInterface::class); - $this->stringifier = $this->prophesize(ShortUrlStringifierInterface::class); + $this->visitsHelper = $this->createMock(VisitsStatsHelperInterface::class); + $this->stringifier = $this->createMock(ShortUrlStringifierInterface::class); $this->commandTester = $this->testerForCommand( - new GetNonOrphanVisitsCommand($this->visitsHelper->reveal(), $this->stringifier->reveal()), + new GetNonOrphanVisitsCommand($this->visitsHelper, $this->stringifier), ); } @@ -45,10 +44,12 @@ class GetNonOrphanVisitsCommandTest extends TestCase $visit = Visit::forValidShortUrl($shortUrl, new Visitor('bar', 'foo', '', ''))->locate( VisitLocation::fromGeolocation(new Location('', 'Spain', '', 'Madrid', 0, 0, '')), ); - $getVisits = $this->visitsHelper->nonOrphanVisits(Argument::any())->willReturn( + $this->visitsHelper->expects($this->once())->method('nonOrphanVisits')->withAnyParameters()->willReturn( new Paginator(new ArrayAdapter([$visit])), ); - $stringify = $this->stringifier->stringify($shortUrl)->willReturn('the_short_url'); + $this->stringifier->expects($this->once())->method('stringify')->with($this->equalTo($shortUrl))->willReturn( + 'the_short_url', + ); $this->commandTester->execute([]); $output = $this->commandTester->getDisplay(); @@ -64,7 +65,5 @@ class GetNonOrphanVisitsCommandTest extends TestCase OUTPUT, $output, ); - $getVisits->shouldHaveBeenCalledOnce(); - $stringify->shouldHaveBeenCalledOnce(); } } From e2986a7b4c56bd9597f716388de8fc4837c18aac Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sat, 22 Oct 2022 14:06:54 +0200 Subject: [PATCH 21/27] Migrated GetOrphanVisitsCommandTest to use PHPUnit mocks --- .../Command/Visit/GetOrphanVisitsCommandTest.php | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/module/CLI/test/Command/Visit/GetOrphanVisitsCommandTest.php b/module/CLI/test/Command/Visit/GetOrphanVisitsCommandTest.php index 8c4a717d..71f8f7b2 100644 --- a/module/CLI/test/Command/Visit/GetOrphanVisitsCommandTest.php +++ b/module/CLI/test/Command/Visit/GetOrphanVisitsCommandTest.php @@ -5,9 +5,8 @@ declare(strict_types=1); namespace ShlinkioTest\Shlink\CLI\Command\Visit; use Pagerfanta\Adapter\ArrayAdapter; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; -use Prophecy\Argument; -use Prophecy\Prophecy\ObjectProphecy; use Shlinkio\Shlink\CLI\Command\Visit\GetOrphanVisitsCommand; use Shlinkio\Shlink\Common\Paginator\Paginator; use Shlinkio\Shlink\Core\Visit\Entity\Visit; @@ -23,12 +22,12 @@ class GetOrphanVisitsCommandTest extends TestCase use CliTestUtilsTrait; private CommandTester $commandTester; - private ObjectProphecy $visitsHelper; + private MockObject $visitsHelper; protected function setUp(): void { - $this->visitsHelper = $this->prophesize(VisitsStatsHelperInterface::class); - $this->commandTester = $this->testerForCommand(new GetOrphanVisitsCommand($this->visitsHelper->reveal())); + $this->visitsHelper = $this->createMock(VisitsStatsHelperInterface::class); + $this->commandTester = $this->testerForCommand(new GetOrphanVisitsCommand($this->visitsHelper)); } /** @test */ @@ -37,7 +36,7 @@ class GetOrphanVisitsCommandTest extends TestCase $visit = Visit::forBasePath(new Visitor('bar', 'foo', '', ''))->locate( VisitLocation::fromGeolocation(new Location('', 'Spain', '', 'Madrid', 0, 0, '')), ); - $getVisits = $this->visitsHelper->orphanVisits(Argument::any())->willReturn( + $this->visitsHelper->expects($this->once())->method('orphanVisits')->withAnyParameters()->willReturn( new Paginator(new ArrayAdapter([$visit])), ); @@ -55,6 +54,5 @@ class GetOrphanVisitsCommandTest extends TestCase OUTPUT, $output, ); - $getVisits->shouldHaveBeenCalledOnce(); } } From 4b3ed2b7ba85a6db0ddce9b1a0174dedbc4d17ff Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sat, 22 Oct 2022 14:16:42 +0200 Subject: [PATCH 22/27] Migrated LocateVisitsCommandTest to use PHPUnit mocks --- .../Command/Visit/LocateVisitsCommandTest.php | 99 +++++++++---------- 1 file changed, 44 insertions(+), 55 deletions(-) diff --git a/module/CLI/test/Command/Visit/LocateVisitsCommandTest.php b/module/CLI/test/Command/Visit/LocateVisitsCommandTest.php index 0db2f493..418e3af6 100644 --- a/module/CLI/test/Command/Visit/LocateVisitsCommandTest.php +++ b/module/CLI/test/Command/Visit/LocateVisitsCommandTest.php @@ -6,8 +6,6 @@ namespace ShlinkioTest\Shlink\CLI\Command\Visit; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; -use Prophecy\Argument; -use Prophecy\Prophecy\ObjectProphecy; use Shlinkio\Shlink\CLI\Command\Visit\DownloadGeoLiteDbCommand; use Shlinkio\Shlink\CLI\Command\Visit\LocateVisitsCommand; use Shlinkio\Shlink\CLI\Util\ExitCodes; @@ -36,28 +34,21 @@ class LocateVisitsCommandTest extends TestCase use CliTestUtilsTrait; private CommandTester $commandTester; - private ObjectProphecy $visitService; - private ObjectProphecy $visitToLocation; - private ObjectProphecy $lock; + private MockObject $visitService; + private MockObject $visitToLocation; + private MockObject $lock; private MockObject $downloadDbCommand; protected function setUp(): void { - $this->visitService = $this->prophesize(VisitLocator::class); - $this->visitToLocation = $this->prophesize(VisitToLocationHelperInterface::class); + $this->visitService = $this->createMock(VisitLocator::class); + $this->visitToLocation = $this->createMock(VisitToLocationHelperInterface::class); - $locker = $this->prophesize(Lock\LockFactory::class); - $this->lock = $this->prophesize(Lock\LockInterface::class); - $this->lock->acquire(false)->willReturn(true); - $this->lock->release()->will(function (): void { - }); - $locker->createLock(Argument::type('string'), 600.0, false)->willReturn($this->lock->reveal()); + $locker = $this->createMock(Lock\LockFactory::class); + $this->lock = $this->createMock(Lock\LockInterface::class); + $locker->method('createLock')->with($this->isType('string'), 600.0, false)->willReturn($this->lock); - $command = new LocateVisitsCommand( - $this->visitService->reveal(), - $this->visitToLocation->reveal(), - $locker->reveal(), - ); + $command = new LocateVisitsCommand($this->visitService, $this->visitToLocation, $locker); $this->downloadDbCommand = $this->createCommandMock(DownloadGeoLiteDbCommand::NAME); $this->commandTester = $this->testerForCommand($command, $this->downloadDbCommand); @@ -78,14 +69,22 @@ class LocateVisitsCommandTest extends TestCase $location = VisitLocation::fromGeolocation(Location::emptyInstance()); $mockMethodBehavior = $this->invokeHelperMethods($visit, $location); - $locateVisits = $this->visitService->locateUnlocatedVisits(Argument::cetera())->will($mockMethodBehavior); - $locateEmptyVisits = $this->visitService->locateVisitsWithEmptyLocation(Argument::cetera())->will( - $mockMethodBehavior, - ); - $locateAllVisits = $this->visitService->locateAllVisits(Argument::cetera())->will($mockMethodBehavior); - $resolveIpLocation = $this->visitToLocation->resolveVisitLocation(Argument::any())->willReturn( - Location::emptyInstance(), - ); + $this->lock->method('acquire')->with($this->isFalse())->willReturn(true); + $this->visitService->expects($this->exactly($expectedUnlocatedCalls)) + ->method('locateUnlocatedVisits') + ->withAnyParameters() + ->willReturnCallback($mockMethodBehavior); + $this->visitService->expects($this->exactly($expectedEmptyCalls)) + ->method('locateVisitsWithEmptyLocation') + ->withAnyParameters() + ->willReturnCallback($mockMethodBehavior); + $this->visitService->expects($this->exactly($expectedAllCalls)) + ->method('locateAllVisits') + ->withAnyParameters() + ->willReturnCallback($mockMethodBehavior); + $this->visitToLocation->expects( + $this->exactly($expectedUnlocatedCalls + $expectedEmptyCalls + $expectedAllCalls), + )->method('resolveVisitLocation')->withAnyParameters()->willReturn(Location::emptyInstance()); $this->downloadDbCommand->method('run')->withAnyParameters()->willReturn(ExitCodes::EXIT_SUCCESS); $this->commandTester->setInputs(['y']); @@ -98,12 +97,6 @@ class LocateVisitsCommandTest extends TestCase } else { self::assertStringNotContainsString('Continue at your own', $output); } - $locateVisits->shouldHaveBeenCalledTimes($expectedUnlocatedCalls); - $locateEmptyVisits->shouldHaveBeenCalledTimes($expectedEmptyCalls); - $locateAllVisits->shouldHaveBeenCalledTimes($expectedAllCalls); - $resolveIpLocation->shouldHaveBeenCalledTimes( - $expectedUnlocatedCalls + $expectedEmptyCalls + $expectedAllCalls, - ); } public function provideArgs(): iterable @@ -122,10 +115,12 @@ class LocateVisitsCommandTest extends TestCase $visit = Visit::forValidShortUrl(ShortUrl::createEmpty(), Visitor::emptyInstance()); $location = VisitLocation::fromGeolocation(Location::emptyInstance()); - $locateVisits = $this->visitService->locateUnlocatedVisits(Argument::cetera())->will( - $this->invokeHelperMethods($visit, $location), - ); - $resolveIpLocation = $this->visitToLocation->resolveVisitLocation(Argument::any())->willThrow($e); + $this->lock->method('acquire')->with($this->isFalse())->willReturn(true); + $this->visitService->expects($this->once()) + ->method('locateUnlocatedVisits') + ->withAnyParameters() + ->willReturnCallback($this->invokeHelperMethods($visit, $location)); + $this->visitToLocation->expects($this->once())->method('resolveVisitLocation')->willThrowException($e); $this->downloadDbCommand->method('run')->withAnyParameters()->willReturn(ExitCodes::EXIT_SUCCESS); $this->commandTester->execute([], ['verbosity' => OutputInterface::VERBOSITY_VERBOSE]); @@ -133,8 +128,6 @@ class LocateVisitsCommandTest extends TestCase $output = $this->commandTester->getDisplay(); self::assertStringContainsString('Processing IP', $output); self::assertStringContainsString($message, $output); - $locateVisits->shouldHaveBeenCalledOnce(); - $resolveIpLocation->shouldHaveBeenCalledOnce(); } public function provideIgnoredAddresses(): iterable @@ -149,10 +142,12 @@ class LocateVisitsCommandTest extends TestCase $visit = Visit::forValidShortUrl(ShortUrl::createEmpty(), new Visitor('', '', '1.2.3.4', '')); $location = VisitLocation::fromGeolocation(Location::emptyInstance()); - $locateVisits = $this->visitService->locateUnlocatedVisits(Argument::cetera())->will( - $this->invokeHelperMethods($visit, $location), - ); - $resolveIpLocation = $this->visitToLocation->resolveVisitLocation(Argument::any())->willThrow( + $this->lock->method('acquire')->with($this->isFalse())->willReturn(true); + $this->visitService->expects($this->once()) + ->method('locateUnlocatedVisits') + ->withAnyParameters() + ->willReturnCallback($this->invokeHelperMethods($visit, $location)); + $this->visitToLocation->expects($this->once())->method('resolveVisitLocation')->willThrowException( IpCannotBeLocatedException::forError(WrongIpException::fromIpAddress('1.2.3.4')), ); $this->downloadDbCommand->method('run')->withAnyParameters()->willReturn(ExitCodes::EXIT_SUCCESS); @@ -162,16 +157,11 @@ class LocateVisitsCommandTest extends TestCase $output = $this->commandTester->getDisplay(); self::assertStringContainsString('An error occurred while locating IP. Skipped', $output); - $locateVisits->shouldHaveBeenCalledOnce(); - $resolveIpLocation->shouldHaveBeenCalledOnce(); } private function invokeHelperMethods(Visit $visit, VisitLocation $location): callable { - return function (array $args) use ($visit, $location): void { - /** @var VisitGeolocationHelperInterface $helper */ - [$helper] = $args; - + return static function (VisitGeolocationHelperInterface $helper) use ($visit, $location): void { $helper->geolocateVisit($visit); $helper->onVisitLocated($location, $visit); }; @@ -180,11 +170,10 @@ class LocateVisitsCommandTest extends TestCase /** @test */ public function noActionIsPerformedIfLockIsAcquired(): void { - $this->lock->acquire(false)->willReturn(false); + $this->lock->method('acquire')->with($this->isFalse())->willReturn(false); - $locateVisits = $this->visitService->locateUnlocatedVisits(Argument::cetera())->will(function (): void { - }); - $resolveIpLocation = $this->visitToLocation->resolveVisitLocation(Argument::any()); + $this->visitService->expects($this->never())->method('locateUnlocatedVisits'); + $this->visitToLocation->expects($this->never())->method('resolveVisitLocation'); $this->downloadDbCommand->method('run')->withAnyParameters()->willReturn(ExitCodes::EXIT_SUCCESS); $this->commandTester->execute([], ['verbosity' => OutputInterface::VERBOSITY_VERBOSE]); @@ -194,25 +183,25 @@ class LocateVisitsCommandTest extends TestCase sprintf('Command "%s" is already in progress. Skipping.', LocateVisitsCommand::NAME), $output, ); - $locateVisits->shouldNotHaveBeenCalled(); - $resolveIpLocation->shouldNotHaveBeenCalled(); } /** @test */ public function showsProperMessageWhenGeoLiteUpdateFails(): void { + $this->lock->method('acquire')->with($this->isFalse())->willReturn(true); $this->downloadDbCommand->method('run')->withAnyParameters()->willReturn(ExitCodes::EXIT_FAILURE); + $this->visitService->expects($this->never())->method('locateUnlocatedVisits'); $this->commandTester->execute([]); $output = $this->commandTester->getDisplay(); self::assertStringContainsString('It is not possible to locate visits without a GeoLite2 db file.', $output); - $this->visitService->locateUnlocatedVisits(Argument::cetera())->shouldNotHaveBeenCalled(); } /** @test */ public function providingAllFlagOnItsOwnDisplaysNotice(): void { + $this->lock->method('acquire')->with($this->isFalse())->willReturn(true); $this->downloadDbCommand->method('run')->withAnyParameters()->willReturn(ExitCodes::EXIT_SUCCESS); $this->commandTester->execute(['--all' => true]); From a484455b0bd04e8b13d26b96a3cd22843656af28 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sat, 22 Oct 2022 14:27:07 +0200 Subject: [PATCH 23/27] Migrated GeolocationDbUpdaterTest to use PHPUnit mocks --- .../test/GeoLite/GeolocationDbUpdaterTest.php | 95 ++++++++----------- 1 file changed, 39 insertions(+), 56 deletions(-) diff --git a/module/CLI/test/GeoLite/GeolocationDbUpdaterTest.php b/module/CLI/test/GeoLite/GeolocationDbUpdaterTest.php index 61056922..0b890e46 100644 --- a/module/CLI/test/GeoLite/GeolocationDbUpdaterTest.php +++ b/module/CLI/test/GeoLite/GeolocationDbUpdaterTest.php @@ -7,10 +7,8 @@ namespace ShlinkioTest\Shlink\CLI\GeoLite; use Cake\Chronos\Chronos; use GeoIp2\Database\Reader; use MaxMind\Db\Reader\Metadata; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; -use Prophecy\Argument; -use Prophecy\PhpUnit\ProphecyTrait; -use Prophecy\Prophecy\ObjectProphecy; use Shlinkio\Shlink\CLI\Exception\GeolocationDbUpdateFailedException; use Shlinkio\Shlink\CLI\GeoLite\GeolocationDbUpdater; use Shlinkio\Shlink\CLI\GeoLite\GeolocationResult; @@ -25,23 +23,18 @@ use function range; class GeolocationDbUpdaterTest extends TestCase { - use ProphecyTrait; - - private GeolocationDbUpdater $geolocationDbUpdater; - private ObjectProphecy $dbUpdater; - private ObjectProphecy $geoLiteDbReader; - private ObjectProphecy $lock; + private MockObject $dbUpdater; + private MockObject $geoLiteDbReader; + private MockObject $lock; protected function setUp(): void { - $this->dbUpdater = $this->prophesize(DbUpdaterInterface::class); - $this->geoLiteDbReader = $this->prophesize(Reader::class); + $this->dbUpdater = $this->createMock(DbUpdaterInterface::class); + $this->geoLiteDbReader = $this->createMock(Reader::class); $this->trackingOptions = new TrackingOptions(); - $this->lock = $this->prophesize(Lock\LockInterface::class); - $this->lock->acquire(true)->willReturn(true); - $this->lock->release()->will(function (): void { - }); + $this->lock = $this->createMock(Lock\LockInterface::class); + $this->lock->method('acquire')->with($this->isTrue())->willReturn(true); } /** @test */ @@ -50,25 +43,21 @@ class GeolocationDbUpdaterTest extends TestCase $mustBeUpdated = fn () => self::assertTrue(true); $prev = new DbUpdateException(''); - $fileExists = $this->dbUpdater->databaseFileExists()->willReturn(false); - $getMeta = $this->geoLiteDbReader->metadata(); - $download = $this->dbUpdater->downloadFreshCopy(null)->willThrow($prev); + $this->dbUpdater->expects($this->once())->method('databaseFileExists')->willReturn(false); + $this->dbUpdater->expects($this->once())->method('downloadFreshCopy')->with( + $this->isNull(), + )->willThrowException($prev); + $this->geoLiteDbReader->expects($this->never())->method('metadata'); try { $this->geolocationDbUpdater()->checkDbUpdate($mustBeUpdated); - self::assertTrue(false); // If this is reached, the test will fail + self::fail(); } catch (Throwable $e) { /** @var GeolocationDbUpdateFailedException $e */ self::assertInstanceOf(GeolocationDbUpdateFailedException::class, $e); self::assertSame($prev, $e->getPrevious()); self::assertFalse($e->olderDbExists()); } - - $fileExists->shouldHaveBeenCalledOnce(); - $getMeta->shouldNotHaveBeenCalled(); - $download->shouldHaveBeenCalledOnce(); - $this->lock->acquire(true)->shouldHaveBeenCalledOnce(); - $this->lock->release()->shouldHaveBeenCalledOnce(); } /** @@ -77,26 +66,24 @@ class GeolocationDbUpdaterTest extends TestCase */ public function exceptionIsThrownWhenOlderDbIsTooOldAndDownloadFails(int $days): void { - $fileExists = $this->dbUpdater->databaseFileExists()->willReturn(true); - $getMeta = $this->geoLiteDbReader->metadata()->willReturn($this->buildMetaWithBuildEpoch( - Chronos::now()->subDays($days)->getTimestamp(), - )); $prev = new DbUpdateException(''); - $download = $this->dbUpdater->downloadFreshCopy(null)->willThrow($prev); + $this->dbUpdater->expects($this->once())->method('databaseFileExists')->willReturn(true); + $this->dbUpdater->expects($this->once())->method('downloadFreshCopy')->with( + $this->isNull(), + )->willThrowException($prev); + $this->geoLiteDbReader->expects($this->once())->method('metadata')->with()->willReturn( + $this->buildMetaWithBuildEpoch(Chronos::now()->subDays($days)->getTimestamp()), + ); try { $this->geolocationDbUpdater()->checkDbUpdate(); - self::assertTrue(false); // If this is reached, the test will fail + self::fail(); } catch (Throwable $e) { /** @var GeolocationDbUpdateFailedException $e */ self::assertInstanceOf(GeolocationDbUpdateFailedException::class, $e); self::assertSame($prev, $e->getPrevious()); self::assertTrue($e->olderDbExists()); } - - $fileExists->shouldHaveBeenCalledOnce(); - $getMeta->shouldHaveBeenCalledOnce(); - $download->shouldHaveBeenCalledOnce(); } public function provideBigDays(): iterable @@ -113,17 +100,15 @@ class GeolocationDbUpdaterTest extends TestCase */ public function databaseIsNotUpdatedIfItIsNewEnough(string|int $buildEpoch): void { - $fileExists = $this->dbUpdater->databaseFileExists()->willReturn(true); - $getMeta = $this->geoLiteDbReader->metadata()->willReturn($this->buildMetaWithBuildEpoch($buildEpoch)); - $download = $this->dbUpdater->downloadFreshCopy(null)->will(function (): void { - }); + $this->dbUpdater->expects($this->once())->method('databaseFileExists')->willReturn(true); + $this->dbUpdater->expects($this->never())->method('downloadFreshCopy'); + $this->geoLiteDbReader->expects($this->once())->method('metadata')->with()->willReturn( + $this->buildMetaWithBuildEpoch($buildEpoch), + ); $result = $this->geolocationDbUpdater()->checkDbUpdate(); self::assertEquals(GeolocationResult::DB_IS_UP_TO_DATE, $result); - $fileExists->shouldHaveBeenCalledOnce(); - $getMeta->shouldHaveBeenCalledOnce(); - $download->shouldNotHaveBeenCalled(); } public function provideSmallDays(): iterable @@ -139,18 +124,16 @@ class GeolocationDbUpdaterTest extends TestCase /** @test */ public function exceptionIsThrownWhenCheckingExistingDatabaseWithInvalidBuildEpoch(): void { - $fileExists = $this->dbUpdater->databaseFileExists()->willReturn(true); - $getMeta = $this->geoLiteDbReader->metadata()->willReturn($this->buildMetaWithBuildEpoch('invalid')); - $download = $this->dbUpdater->downloadFreshCopy(null)->will(function (): void { - }); + $this->dbUpdater->expects($this->once())->method('databaseFileExists')->willReturn(true); + $this->dbUpdater->expects($this->never())->method('downloadFreshCopy'); + $this->geoLiteDbReader->expects($this->once())->method('metadata')->with()->willReturn( + $this->buildMetaWithBuildEpoch('invalid'), + ); $this->expectException(GeolocationDbUpdateFailedException::class); $this->expectExceptionMessage( 'Build epoch with value "invalid" from existing geolocation database, could not be parsed to integer.', ); - $fileExists->shouldBeCalledOnce(); - $getMeta->shouldBeCalledOnce(); - $download->shouldNotBeCalled(); $this->geolocationDbUpdater()->checkDbUpdate(); } @@ -177,10 +160,10 @@ class GeolocationDbUpdaterTest extends TestCase public function downloadDbIsSkippedIfTrackingIsDisabled(TrackingOptions $options): void { $result = $this->geolocationDbUpdater($options)->checkDbUpdate(); + $this->dbUpdater->expects($this->never())->method('databaseFileExists'); + $this->geoLiteDbReader->expects($this->never())->method('metadata'); self::assertEquals(GeolocationResult::CHECK_SKIPPED, $result); - $this->dbUpdater->databaseFileExists(Argument::cetera())->shouldNotHaveBeenCalled(); - $this->geoLiteDbReader->metadata(Argument::cetera())->shouldNotHaveBeenCalled(); } public function provideTrackingOptions(): iterable @@ -192,13 +175,13 @@ class GeolocationDbUpdaterTest extends TestCase private function geolocationDbUpdater(?TrackingOptions $options = null): GeolocationDbUpdater { - $locker = $this->prophesize(Lock\LockFactory::class); - $locker->createLock(Argument::type('string'))->willReturn($this->lock->reveal()); + $locker = $this->createMock(Lock\LockFactory::class); + $locker->method('createLock')->with($this->isType('string'))->willReturn($this->lock); return new GeolocationDbUpdater( - $this->dbUpdater->reveal(), - $this->geoLiteDbReader->reveal(), - $locker->reveal(), + $this->dbUpdater, + $this->geoLiteDbReader, + $locker, $options ?? new TrackingOptions(), ); } From 4cb44be9a0c0a507f2d8afb8aa00dd4799fdbebc Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sat, 22 Oct 2022 14:37:13 +0200 Subject: [PATCH 24/27] Migrated ProcessRunnerTest to use PHPUnit mocks --- module/CLI/test/Util/ProcessRunnerTest.php | 111 +++++++++------------ 1 file changed, 46 insertions(+), 65 deletions(-) diff --git a/module/CLI/test/Util/ProcessRunnerTest.php b/module/CLI/test/Util/ProcessRunnerTest.php index 05ac5dd7..276b5abb 100644 --- a/module/CLI/test/Util/ProcessRunnerTest.php +++ b/module/CLI/test/Util/ProcessRunnerTest.php @@ -4,10 +4,8 @@ declare(strict_types=1); namespace ShlinkioTest\Shlink\CLI\Util; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; -use Prophecy\Argument; -use Prophecy\PhpUnit\ProphecyTrait; -use Prophecy\Prophecy\ObjectProphecy; use Shlinkio\Shlink\CLI\Util\ProcessRunner; use Symfony\Component\Console\Helper\DebugFormatterHelper; use Symfony\Component\Console\Helper\HelperSet; @@ -17,90 +15,73 @@ use Symfony\Component\Process\Process; class ProcessRunnerTest extends TestCase { - use ProphecyTrait; - private ProcessRunner $runner; - private ObjectProphecy $helper; - private ObjectProphecy $formatter; - private ObjectProphecy $process; - private ObjectProphecy $output; + private MockObject $helper; + private MockObject $formatter; + private MockObject $process; + private MockObject $output; protected function setUp(): void { - $this->helper = $this->prophesize(ProcessHelper::class); - $this->formatter = $this->prophesize(DebugFormatterHelper::class); - $helperSet = $this->prophesize(HelperSet::class); - $helperSet->get('debug_formatter')->willReturn($this->formatter->reveal()); - $this->helper->getHelperSet()->willReturn($helperSet->reveal()); - $this->process = $this->prophesize(Process::class); + $this->helper = $this->createMock(ProcessHelper::class); + $this->formatter = $this->createMock(DebugFormatterHelper::class); + $helperSet = $this->createMock(HelperSet::class); + $helperSet->method('get')->with($this->equalTo('debug_formatter'))->willReturn($this->formatter); + $this->helper->method('getHelperSet')->with()->willReturn($helperSet); + $this->process = $this->createMock(Process::class); + $this->output = $this->createMock(OutputInterface::class); - $this->runner = new ProcessRunner($this->helper->reveal(), fn () => $this->process->reveal()); - $this->output = $this->prophesize(OutputInterface::class); + $this->runner = new ProcessRunner($this->helper, fn () => $this->process); } /** @test */ public function noMessagesAreWrittenWhenOutputIsNotVerbose(): void { - $isVeryVerbose = $this->output->isVeryVerbose()->willReturn(false); - $isDebug = $this->output->isDebug()->willReturn(false); - $mustRun = $this->process->mustRun(Argument::cetera())->willReturn($this->process->reveal()); + $this->output->expects($this->exactly(2))->method('isVeryVerbose')->with()->willReturn(false); + $this->output->expects($this->once())->method('isDebug')->with()->willReturn(false); + $this->output->expects($this->never())->method('write'); + $this->process->expects($this->once())->method('mustRun')->withAnyParameters()->willReturn($this->process); + $this->process->expects($this->never())->method('isSuccessful'); + $this->process->expects($this->never())->method('getCommandLine'); + $this->helper->expects($this->never())->method('wrapCallback'); + $this->formatter->expects($this->never())->method('start'); + $this->formatter->expects($this->never())->method('stop'); - $this->runner->run($this->output->reveal(), []); - - $isVeryVerbose->shouldHaveBeenCalledTimes(2); - $isDebug->shouldHaveBeenCalledOnce(); - $mustRun->shouldHaveBeenCalledOnce(); - $this->process->isSuccessful()->shouldNotHaveBeenCalled(); - $this->process->getCommandLine()->shouldNotHaveBeenCalled(); - $this->output->write(Argument::cetera())->shouldNotHaveBeenCalled(); - $this->helper->wrapCallback(Argument::cetera())->shouldNotHaveBeenCalled(); - $this->formatter->start(Argument::cetera())->shouldNotHaveBeenCalled(); - $this->formatter->stop(Argument::cetera())->shouldNotHaveBeenCalled(); + $this->runner->run($this->output, []); } /** @test */ public function someMessagesAreWrittenWhenOutputIsVerbose(): void { - $isVeryVerbose = $this->output->isVeryVerbose()->willReturn(true); - $isDebug = $this->output->isDebug()->willReturn(false); - $mustRun = $this->process->mustRun(Argument::cetera())->willReturn($this->process->reveal()); - $isSuccessful = $this->process->isSuccessful()->willReturn(true); - $getCommandLine = $this->process->getCommandLine()->willReturn('true'); - $start = $this->formatter->start(Argument::cetera())->willReturn(''); - $stop = $this->formatter->stop(Argument::cetera())->willReturn(''); + $this->output->expects($this->exactly(2))->method('isVeryVerbose')->with()->willReturn(true); + $this->output->expects($this->once())->method('isDebug')->with()->willReturn(false); + $this->output->expects($this->exactly(2))->method('write')->withAnyParameters(); + $this->process->expects($this->once())->method('mustRun')->withAnyParameters()->willReturn($this->process); + $this->process->expects($this->exactly(2))->method('isSuccessful')->with()->willReturn(true); + $this->process->expects($this->once())->method('getCommandLine')->with()->willReturn('true'); + $this->formatter->expects($this->once())->method('start')->withAnyParameters()->willReturn(''); + $this->formatter->expects($this->once())->method('stop')->withAnyParameters()->willReturn(''); + $this->helper->expects($this->never())->method('wrapCallback'); - $this->runner->run($this->output->reveal(), []); - - $isVeryVerbose->shouldHaveBeenCalledTimes(2); - $isDebug->shouldHaveBeenCalledOnce(); - $mustRun->shouldHaveBeenCalledOnce(); - $this->output->write(Argument::cetera())->shouldHaveBeenCalledTimes(2); - $this->helper->wrapCallback(Argument::cetera())->shouldNotHaveBeenCalled(); - $isSuccessful->shouldHaveBeenCalledTimes(2); - $getCommandLine->shouldHaveBeenCalledOnce(); - $start->shouldHaveBeenCalledOnce(); - $stop->shouldHaveBeenCalledOnce(); + $this->runner->run($this->output, []); } /** @test */ public function wrapsCallbackWhenOutputIsDebug(): void { - $isVeryVerbose = $this->output->isVeryVerbose()->willReturn(false); - $isDebug = $this->output->isDebug()->willReturn(true); - $mustRun = $this->process->mustRun(Argument::cetera())->willReturn($this->process->reveal()); - $wrapCallback = $this->helper->wrapCallback(Argument::cetera())->willReturn(function (): void { - }); + $this->output->expects($this->exactly(2))->method('isVeryVerbose')->with()->willReturn(false); + $this->output->expects($this->once())->method('isDebug')->with()->willReturn(true); + $this->output->expects($this->never())->method('write'); + $this->process->expects($this->once())->method('mustRun')->withAnyParameters()->willReturn($this->process); + $this->process->expects($this->never())->method('isSuccessful'); + $this->process->expects($this->never())->method('getCommandLine'); + $this->helper->expects($this->once())->method('wrapCallback')->withAnyParameters()->willReturn( + function (): void { + }, + ); + $this->formatter->expects($this->never())->method('start'); + $this->formatter->expects($this->never())->method('stop'); - $this->runner->run($this->output->reveal(), []); - - $isVeryVerbose->shouldHaveBeenCalledTimes(2); - $isDebug->shouldHaveBeenCalledOnce(); - $mustRun->shouldHaveBeenCalledOnce(); - $wrapCallback->shouldHaveBeenCalledOnce(); - $this->process->isSuccessful()->shouldNotHaveBeenCalled(); - $this->process->getCommandLine()->shouldNotHaveBeenCalled(); - $this->output->write(Argument::cetera())->shouldNotHaveBeenCalled(); - $this->formatter->start(Argument::cetera())->shouldNotHaveBeenCalled(); - $this->formatter->stop(Argument::cetera())->shouldNotHaveBeenCalled(); + $this->runner->run($this->output, []); } } From 32417e40cbe527d9e520594fb74fe6a80110cc53 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sat, 22 Oct 2022 14:40:35 +0200 Subject: [PATCH 25/27] Migrated ShlinkTableTest to use PHPUnit mocks --- module/CLI/test/Util/ProcessRunnerTest.php | 6 +-- module/CLI/test/Util/ShlinkTableTest.php | 43 ++++++++++------------ 2 files changed, 22 insertions(+), 27 deletions(-) diff --git a/module/CLI/test/Util/ProcessRunnerTest.php b/module/CLI/test/Util/ProcessRunnerTest.php index 276b5abb..3e2e5ed0 100644 --- a/module/CLI/test/Util/ProcessRunnerTest.php +++ b/module/CLI/test/Util/ProcessRunnerTest.php @@ -40,7 +40,7 @@ class ProcessRunnerTest extends TestCase $this->output->expects($this->exactly(2))->method('isVeryVerbose')->with()->willReturn(false); $this->output->expects($this->once())->method('isDebug')->with()->willReturn(false); $this->output->expects($this->never())->method('write'); - $this->process->expects($this->once())->method('mustRun')->withAnyParameters()->willReturn($this->process); + $this->process->expects($this->once())->method('mustRun')->withAnyParameters()->willReturnSelf(); $this->process->expects($this->never())->method('isSuccessful'); $this->process->expects($this->never())->method('getCommandLine'); $this->helper->expects($this->never())->method('wrapCallback'); @@ -56,7 +56,7 @@ class ProcessRunnerTest extends TestCase $this->output->expects($this->exactly(2))->method('isVeryVerbose')->with()->willReturn(true); $this->output->expects($this->once())->method('isDebug')->with()->willReturn(false); $this->output->expects($this->exactly(2))->method('write')->withAnyParameters(); - $this->process->expects($this->once())->method('mustRun')->withAnyParameters()->willReturn($this->process); + $this->process->expects($this->once())->method('mustRun')->withAnyParameters()->willReturnSelf(); $this->process->expects($this->exactly(2))->method('isSuccessful')->with()->willReturn(true); $this->process->expects($this->once())->method('getCommandLine')->with()->willReturn('true'); $this->formatter->expects($this->once())->method('start')->withAnyParameters()->willReturn(''); @@ -72,7 +72,7 @@ class ProcessRunnerTest extends TestCase $this->output->expects($this->exactly(2))->method('isVeryVerbose')->with()->willReturn(false); $this->output->expects($this->once())->method('isDebug')->with()->willReturn(true); $this->output->expects($this->never())->method('write'); - $this->process->expects($this->once())->method('mustRun')->withAnyParameters()->willReturn($this->process); + $this->process->expects($this->once())->method('mustRun')->withAnyParameters()->willReturnSelf(); $this->process->expects($this->never())->method('isSuccessful'); $this->process->expects($this->never())->method('getCommandLine'); $this->helper->expects($this->once())->method('wrapCallback')->withAnyParameters()->willReturn( diff --git a/module/CLI/test/Util/ShlinkTableTest.php b/module/CLI/test/Util/ShlinkTableTest.php index ffe1f30d..a9a6f3f5 100644 --- a/module/CLI/test/Util/ShlinkTableTest.php +++ b/module/CLI/test/Util/ShlinkTableTest.php @@ -4,10 +4,8 @@ declare(strict_types=1); namespace ShlinkioTest\Shlink\CLI\Util; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; -use Prophecy\Argument; -use Prophecy\PhpUnit\ProphecyTrait; -use Prophecy\Prophecy\ObjectProphecy; use ReflectionObject; use Shlinkio\Shlink\CLI\Util\ShlinkTable; use Symfony\Component\Console\Helper\Table; @@ -16,15 +14,13 @@ use Symfony\Component\Console\Output\OutputInterface; class ShlinkTableTest extends TestCase { - use ProphecyTrait; - private ShlinkTable $shlinkTable; - private ObjectProphecy $baseTable; + private MockObject $baseTable; protected function setUp(): void { - $this->baseTable = $this->prophesize(Table::class); - $this->shlinkTable = ShlinkTable::fromBaseTable($this->baseTable->reveal()); + $this->baseTable = $this->createMock(Table::class); + $this->shlinkTable = ShlinkTable::fromBaseTable($this->baseTable); } /** @test */ @@ -35,29 +31,28 @@ class ShlinkTableTest extends TestCase $headerTitle = 'Header'; $footerTitle = 'Footer'; - $setStyle = $this->baseTable->setStyle(Argument::type(TableStyle::class))->willReturn( - $this->baseTable->reveal(), - ); - $setHeaders = $this->baseTable->setHeaders($headers)->willReturn($this->baseTable->reveal()); - $setRows = $this->baseTable->setRows($rows)->willReturn($this->baseTable->reveal()); - $setFooterTitle = $this->baseTable->setFooterTitle($footerTitle)->willReturn($this->baseTable->reveal()); - $setHeaderTitle = $this->baseTable->setHeaderTitle($headerTitle)->willReturn($this->baseTable->reveal()); - $render = $this->baseTable->render()->willReturn($this->baseTable->reveal()); + $this->baseTable->expects($this->once())->method('setStyle')->with( + $this->isInstanceOf(TableStyle::class) + )->willReturnSelf(); + $this->baseTable->expects($this->once())->method('setHeaders')->with( + $this->equalTo($headers), + )->willReturnSelf(); + $this->baseTable->expects($this->once())->method('setRows')->with($this->equalTo($rows))->willReturnSelf(); + $this->baseTable->expects($this->once())->method('setFooterTitle')->with( + $this->equalTo($footerTitle), + )->willReturnSelf(); + $this->baseTable->expects($this->once())->method('setHeaderTitle')->with( + $this->equalTo($headerTitle), + )->willReturnSelf(); + $this->baseTable->expects($this->once())->method('render')->with()->willReturnSelf(); $this->shlinkTable->render($headers, $rows, $footerTitle, $headerTitle); - - $setStyle->shouldHaveBeenCalledOnce(); - $setHeaders->shouldHaveBeenCalledOnce(); - $setRows->shouldHaveBeenCalledOnce(); - $setFooterTitle->shouldHaveBeenCalledOnce(); - $setHeaderTitle->shouldHaveBeenCalledOnce(); - $render->shouldHaveBeenCalledOnce(); } /** @test */ public function newTableIsCreatedForFactoryMethod(): void { - $instance = ShlinkTable::default($this->prophesize(OutputInterface::class)->reveal()); + $instance = ShlinkTable::default($this->createMock(OutputInterface::class)); $ref = new ReflectionObject($instance); $baseTable = $ref->getProperty('baseTable'); From 52366b9dd42d0b872869ce6e91063228136df24b Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sat, 22 Oct 2022 14:41:22 +0200 Subject: [PATCH 26/27] Removed last reference to prophecytrait in CLI module --- module/CLI/test/CliTestUtilsTrait.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/module/CLI/test/CliTestUtilsTrait.php b/module/CLI/test/CliTestUtilsTrait.php index e69eb71e..00b493e3 100644 --- a/module/CLI/test/CliTestUtilsTrait.php +++ b/module/CLI/test/CliTestUtilsTrait.php @@ -6,7 +6,6 @@ namespace ShlinkioTest\Shlink\CLI; use PHPUnit\Framework\Assert; use PHPUnit\Framework\MockObject\MockObject; -use Prophecy\PhpUnit\ProphecyTrait; use Symfony\Component\Console\Application; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputDefinition; @@ -14,8 +13,6 @@ use Symfony\Component\Console\Tester\CommandTester; trait CliTestUtilsTrait { - use ProphecyTrait; // TODO Remove - /** * @return MockObject & Command */ From b7671f70da096657191c45044c0ce9800f37c870 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sat, 22 Oct 2022 14:41:42 +0200 Subject: [PATCH 27/27] Fixed coding styles --- module/CLI/test/Command/ShortUrl/ListShortUrlsCommandTest.php | 2 +- module/CLI/test/Command/ShortUrl/ResolveUrlCommandTest.php | 2 +- module/CLI/test/Util/ShlinkTableTest.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/module/CLI/test/Command/ShortUrl/ListShortUrlsCommandTest.php b/module/CLI/test/Command/ShortUrl/ListShortUrlsCommandTest.php index dfd1a028..b1356e11 100644 --- a/module/CLI/test/Command/ShortUrl/ListShortUrlsCommandTest.php +++ b/module/CLI/test/Command/ShortUrl/ListShortUrlsCommandTest.php @@ -73,7 +73,7 @@ class ListShortUrlsCommandTest extends TestCase } $this->shortUrlService->expects($this->once())->method('listShortUrls')->with( - $this->equalTo(ShortUrlsParams::emptyInstance()) + $this->equalTo(ShortUrlsParams::emptyInstance()), )->willReturn(new Paginator(new ArrayAdapter($data))); $this->commandTester->setInputs(['n']); diff --git a/module/CLI/test/Command/ShortUrl/ResolveUrlCommandTest.php b/module/CLI/test/Command/ShortUrl/ResolveUrlCommandTest.php index 3b063ab4..609a921a 100644 --- a/module/CLI/test/Command/ShortUrl/ResolveUrlCommandTest.php +++ b/module/CLI/test/Command/ShortUrl/ResolveUrlCommandTest.php @@ -53,7 +53,7 @@ class ResolveUrlCommandTest extends TestCase $shortCode = $identifier->shortCode; $this->urlResolver->expects($this->once())->method('resolveShortUrl')->with( - $this->equalTo($identifier) + $this->equalTo($identifier), )->willThrowException(ShortUrlNotFoundException::fromNotFound($identifier)); $this->commandTester->execute(['shortCode' => $shortCode]); diff --git a/module/CLI/test/Util/ShlinkTableTest.php b/module/CLI/test/Util/ShlinkTableTest.php index a9a6f3f5..6b7e00a3 100644 --- a/module/CLI/test/Util/ShlinkTableTest.php +++ b/module/CLI/test/Util/ShlinkTableTest.php @@ -32,7 +32,7 @@ class ShlinkTableTest extends TestCase $footerTitle = 'Footer'; $this->baseTable->expects($this->once())->method('setStyle')->with( - $this->isInstanceOf(TableStyle::class) + $this->isInstanceOf(TableStyle::class), )->willReturnSelf(); $this->baseTable->expects($this->once())->method('setHeaders')->with( $this->equalTo($headers),