diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5b44684..099fbea 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -34,7 +34,7 @@ jobs: strategy: fail-fast: false matrix: - php: ['7.3', '7.4', '8.0', '8.1'] + php: ['5.4', '5.5', '5.6', '7.0', '7.1', '7.2', '7.3', '7.4', '8.0', '8.1'] name: PHP ${{ matrix.php }} diff --git a/README.md b/README.md index ee0e1b0..f05bfd5 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,13 @@ composer require --dev phpspec/prophecy-phpunit You can read more about Composer on its [official webpage](https://getcomposer.org). +> :bulb: The package can be safely required and used on PHP 5.4 to current in combination with PHPUnit 4.8.36/5.7.21 to current. +> +> When the PHPUnit `prophesize()` method is natively available and not deprecated (PHPUnit 4.8 - 9.0), the PHPUnit +> native functionality will be used. +> For PHPUnit 9.1 and higher, the extension will kick in and polyfill the functionality which was deprecated in PHPUnit. + + ## How to use it The trait ``ProphecyTrait`` provides a method ``prophesize($classOrInterface = null)`` to use Prophecy. diff --git a/composer.json b/composer.json index 2c48d3b..d085180 100644 --- a/composer.json +++ b/composer.json @@ -14,9 +14,9 @@ "minimum-stability": "dev", "prefer-stable": true, "require": { - "php": "^7.3 || ^8", + "php": "^5.4 || ^7.0 || ^8.0", "phpspec/prophecy": "^1.3", - "phpunit/phpunit":"^9.1" + "phpunit/phpunit": "^4.8.36 || ^5.7.21 || ^6.0 || ^7.0 || ^8.0 || ^9.1" }, "autoload": { "psr-4": { diff --git a/fixtures/Success.php b/fixtures/Success.php index 9fdd98c..9e2248e 100644 --- a/fixtures/Success.php +++ b/fixtures/Success.php @@ -5,6 +5,10 @@ use PHPUnit\Framework\TestCase; use Prophecy\PhpUnit\ProphecyTrait; +/** + * {@internal The code in this file must be PHP cross-version compatible for PHP 5.4 - current + * as this test is also used in the AvailabilityTest.} + */ class Success extends TestCase { use ProphecyTrait; diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 9c251d1..4385840 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -3,7 +3,8 @@ - ./tests/ + ./tests/Availability + ./tests/ diff --git a/src/ProphecyTrait.actual.php b/src/ProphecyTrait.actual.php new file mode 100644 index 0000000..7fb7d54 --- /dev/null +++ b/src/ProphecyTrait.actual.php @@ -0,0 +1,110 @@ +recordDoubledType($classOrInterface); + } + + return $this->getProphet()->prophesize($classOrInterface); + } + + /** + * @postCondition + */ + protected function verifyProphecyDoubles(): void + { + if ($this->prophet === null) { + return; + } + + try { + $this->prophet->checkPredictions(); + } catch (PredictionException $e) { + throw new AssertionFailedError($e->getMessage()); + } finally { + $this->countProphecyAssertions(); + } + } + + /** + * @after + */ + protected function tearDownProphecy(): void + { + if (null !== $this->prophet && !$this->prophecyAssertionsCounted) { + // Some Prophecy assertions may have been done in tests themselves even when a failure happened before checking mock objects. + $this->countProphecyAssertions(); + } + + $this->prophet = null; + } + + /** + * @internal + */ + private function countProphecyAssertions(): void + { + \assert($this instanceof TestCase); + $this->prophecyAssertionsCounted = true; + + foreach ($this->prophet->getProphecies() as $objectProphecy) { + foreach ($objectProphecy->getMethodProphecies() as $methodProphecies) { + foreach ($methodProphecies as $methodProphecy) { + \assert($methodProphecy instanceof MethodProphecy); + + $this->addToAssertionCount(\count($methodProphecy->getCheckedPredictions())); + } + } + } + } + + /** + * @internal + */ + private function getProphet(): Prophet + { + if ($this->prophet === null) { + $this->prophet = new Prophet; + } + + return $this->prophet; + } +} diff --git a/src/ProphecyTrait.empty.php b/src/ProphecyTrait.empty.php new file mode 100644 index 0000000..85c0d89 --- /dev/null +++ b/src/ProphecyTrait.empty.php @@ -0,0 +1,17 @@ +recordDoubledType($classOrInterface); - } - - return $this->getProphet()->prophesize($classOrInterface); - } - - /** - * @postCondition - */ - protected function verifyProphecyDoubles(): void - { - if ($this->prophet === null) { - return; - } - try { - $this->prophet->checkPredictions(); - } catch (PredictionException $e) { - throw new AssertionFailedError($e->getMessage()); - } finally { - $this->countProphecyAssertions(); - } +/** + * Retrieve the PHPUnit version id. + * + * As both the pre-PHPUnit 6 class, as well as the PHPUnit 6+ class contain the `id()` function, + * this should work independently of whether or not another library may have aliased the class. + * + * @internal + * + * @return string Version number as a string. + */ +function getPHPUnitVersion() +{ + if (\class_exists('\PHPUnit\Runner\Version')) { + return Version::id(); } - /** - * @after - */ - protected function tearDownProphecy(): void - { - if (null !== $this->prophet && !$this->prophecyAssertionsCounted) { - // Some Prophecy assertions may have been done in tests themselves even when a failure happened before checking mock objects. - $this->countProphecyAssertions(); - } - - $this->prophet = null; + if (\class_exists('\PHPUnit_Runner_Version')) { + return PHPUnit_Runner_Version::id(); } - /** - * @internal - */ - private function countProphecyAssertions(): void - { - \assert($this instanceof TestCase); - $this->prophecyAssertionsCounted = true; - - foreach ($this->prophet->getProphecies() as $objectProphecy) { - foreach ($objectProphecy->getMethodProphecies() as $methodProphecies) { - foreach ($methodProphecies as $methodProphecy) { - \assert($methodProphecy instanceof MethodProphecy); - - $this->addToAssertionCount(\count($methodProphecy->getCheckedPredictions())); - } - } - } - } + return '0'; +} - /** - * @internal - */ - private function getProphet(): Prophet - { - if ($this->prophet === null) { - $this->prophet = new Prophet; - } - return $this->prophet; - } +if (\version_compare(getPHPUnitVersion(), '9.1.0', '>=')) { + // PHPUnit >= 9.1.0. + require_once __DIR__ . '/ProphecyTrait.actual.php'; +} else { + require_once __DIR__ . '/ProphecyTrait.empty.php'; } diff --git a/tests/Availability/AvailabilityTest.php b/tests/Availability/AvailabilityTest.php new file mode 100644 index 0000000..51f1ac5 --- /dev/null +++ b/tests/Availability/AvailabilityTest.php @@ -0,0 +1,42 @@ +assertTrue(trait_exists('Prophecy\PhpUnit\ProphecyTrait'), 'Failed to assert that the ProphecyTrait is available'); + + $test = new Success('testMethod'); + + $result = $test->run(); + + $this->assertSame(0, $result->errorCount(), 'Error count is not 0'); + $this->assertSame(0, $result->failureCount(), 'Failure count is not 0'); + $this->assertCount(1, $result, 'Result is not 1'); + $this->assertSame(1, $test->getNumAssertions(), 'Number of assertions is not 1'); + } +} diff --git a/tests/ProphecyTraitTest.php b/tests/ProphecyTraitTest.php index 6e6a168..afb0a0d 100644 --- a/tests/ProphecyTraitTest.php +++ b/tests/ProphecyTraitTest.php @@ -11,6 +11,8 @@ /** * @covers \Prophecy\PhpUnit\ProphecyTrait + * + * @requires PHPUnit 9.1 */ final class ProphecyTraitTest extends TestCase {