Skip to content

Allow installation in combination with PHPUnit < 9.1 #38

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }}

Expand Down
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
4 changes: 2 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand Down
4 changes: 4 additions & 0 deletions fixtures/Success.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
3 changes: 2 additions & 1 deletion phpunit.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
<phpunit colors="true" bootstrap="vendor/autoload.php">
<testsuites>
<testsuite name="Prophecy PhpUnit Test Suite">
<directory suffix="Test.php">./tests/</directory>
<directory suffix="Test.php">./tests/Availability</directory>
<directory phpVersion="7.3.0" phpVersionOperator=">=" suffix="Test.php">./tests/</directory>
</testsuite>
</testsuites>

Expand Down
110 changes: 110 additions & 0 deletions src/ProphecyTrait.actual.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
<?php declare(strict_types=1);

namespace Prophecy\PhpUnit;

use PHPUnit\Framework\AssertionFailedError;
use PHPUnit\Framework\TestCase;
use Prophecy\Exception\Doubler\DoubleException;
use Prophecy\Exception\Doubler\InterfaceNotFoundException;
use Prophecy\Exception\Prediction\PredictionException;
use Prophecy\Prophecy\MethodProphecy;
use Prophecy\Prophecy\ObjectProphecy;
use Prophecy\Prophet;

/**
* @mixin TestCase
*/
trait ProphecyTrait
{
/**
* @var Prophet|null
*
* @internal
*/
private $prophet;

/**
* @var bool
*
* @internal
*/
private $prophecyAssertionsCounted = false;

/**
* @throws DoubleException
* @throws InterfaceNotFoundException
*
* @psalm-param class-string|null $classOrInterface
*/
protected function prophesize(?string $classOrInterface = null): ObjectProphecy
{
if (\is_string($classOrInterface)) {
\assert($this instanceof TestCase);
$this->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;
}
}
17 changes: 17 additions & 0 deletions src/ProphecyTrait.empty.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

namespace Prophecy\PhpUnit;

/**
* Prophecy trait for use with PHPUnit 4.x - 9.0.
*
* As this trait is empty, calls to `prophesize()` will automatically fall through
* to PHPUnit itself and will use the PHPUnit native `prophesize()` method.
*
* {@internal The code in this file must be PHP cross-version compatible for PHP 5.4 - current!}
*
* @mixin TestCase
*/
trait ProphecyTrait
{
}
125 changes: 29 additions & 96 deletions src/ProphecyTrait.php
Original file line number Diff line number Diff line change
@@ -1,110 +1,43 @@
<?php declare(strict_types=1);
<?php

namespace Prophecy\PhpUnit;

use PHPUnit\Framework\AssertionFailedError;
use PHPUnit\Framework\TestCase;
use Prophecy\Exception\Doubler\DoubleException;
use Prophecy\Exception\Doubler\InterfaceNotFoundException;
use Prophecy\Exception\Prediction\PredictionException;
use Prophecy\Prophecy\MethodProphecy;
use Prophecy\Prophecy\ObjectProphecy;
use Prophecy\Prophet;
use PHPUnit\Runner\Version;
use PHPUnit_Runner_Version;

/**
* @mixin TestCase
* Load the prophecy trait.
*
* {@internal The code in this file must be PHP cross-version compatible for PHP 5.4 - current!}
*/
trait ProphecyTrait
{
/**
* @var Prophet|null
*
* @internal
*/
private $prophet;

/**
* @var bool
*
* @internal
*/
private $prophecyAssertionsCounted = false;

/**
* @throws DoubleException
* @throws InterfaceNotFoundException
*
* @psalm-param class-string|null $classOrInterface
*/
protected function prophesize(?string $classOrInterface = null): ObjectProphecy
{
if (\is_string($classOrInterface)) {
\assert($this instanceof TestCase);
$this->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';
}
42 changes: 42 additions & 0 deletions tests/Availability/AvailabilityTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php

namespace Prophecy\PhpUnit\Tests\Availability;

use PHPUnit\Framework\TestCase;
use Prophecy\PhpUnit\ProphecyTrait;
use Prophecy\PhpUnit\Tests\Fixtures\Success;

/**
* Testing that the autoloading of the empty `ProphecyTrait` trait for PHPUnit 4.x - 9.0 works correctly.
*
* {@internal The code in this file must be PHP cross-version compatible for PHP 5.4 - current!}
*
* @coversNothing
*/
final class AvailabilityTest extends TestCase
{
/**
* @before
*/
protected function declareTestsuiteConstant()
{
// Define the constant because our tests are running PHPUnit test cases themselves
if (!\defined('PHPUNIT_TESTSUITE')) {
\define('PHPUNIT_TESTSUITE', true);
}
}

public function testSuccessfullyCallingProphesizeMethod()
{
$this->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');
}
}
2 changes: 2 additions & 0 deletions tests/ProphecyTraitTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@

/**
* @covers \Prophecy\PhpUnit\ProphecyTrait
*
* @requires PHPUnit 9.1
*/
final class ProphecyTraitTest extends TestCase
{
Expand Down