Skip to content

Commit

Permalink
PHPUnit 9.6.11 | AssertObjectProperty trait: polyfill the Assert::ass…
Browse files Browse the repository at this point in the history
…ertObject[Not]HasProperty() methods

PHPUnit 10.1.0 introduced the new `Assert::assertObjectHasProperty()` and `Assert::assertObjectNotHasProperty()` methods.

These methods have now been backported to PHPUnit 9.6.11, so should be made available in the PHPUnit Polyfills 1.x series.

This commit:
* Adds two traits with the same name.
    One to polyfill the methods when not available in PHPUnit.
    The other - an empty trait - to allow for `use`-ing the trait in PHPUnit versions in which the methods are already natively available.
* Logic to the custom autoloader which will load the correct trait depending on the PHPUnit version used.
* An availability test and functional tests for the functionality polyfilled.

Includes:
* Adding the new polyfill to the existing `TestCases` classes.

Refs:
* sebastianbergmann/phpunit#5220
* sebastianbergmann/phpunit#5231 (and follow up commits/PRs)
* sebastianbergmann/phpunit#5478

Co-authored-by: Jan-Sverre Riksfjord <jasverix@gmail.com>
Co-authored-by: Sebastian Bergmann <sb@sebastian-bergmann.de>
  • Loading branch information
3 people committed Aug 19, 2023
1 parent f4c425a commit 2810188
Show file tree
Hide file tree
Showing 11 changed files with 637 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .github/workflows/test.yml
Expand Up @@ -56,6 +56,11 @@ jobs:
phpunit: '8.1.6'
coverage: true
experimental: false
- php: '7.4'
# Specifically set at 9.6.10 to test functioning on 9.x before the 9.6.11 assertObject*() backports came in.
phpunit: '9.6.10'
coverage: true
experimental: false
- php: '8.0'
phpunit: '8.5.16'
# PHPUnit 8.x does not support code coverage on PHP 8.x.
Expand Down
15 changes: 15 additions & 0 deletions README.md
Expand Up @@ -481,6 +481,21 @@ The `assertObjectEquals()` assertion was introduced in PHPUnit 9.4.0.

[`Assert::assertObjectEquals()`]: https://docs.phpunit.de/en/9.6/assertions.html#assertobjectequals

#### PHPUnit < 9.6.11: `Yoast\PHPUnitPolyfills\Polyfills\AssertObjectProperty`

Polyfills the following method:
| | |
|---------------------------------------|------------------------------------------|
| `Assert::assertObjectHasProperty()` | `Assert::assertObjectNotHasProperty()` |

These methods were introduced in PHPUnit 10.1.0 as alternatives to the `Assert::assertObjectHasAttribute()` and `Assert::assertObjectNotHasAttribute()` methods, which were hard deprecated (warning) in PHPUnit 9.6.1 and removed in PHPUnit 10.0.0.

These methods were later backported to the PHPUnit 9 branch and included in the PHPUnit 9.6.11 release.

<!--
COMMENT: No documentation available (yet) for these assertions on the PHPUnit site.
-->


### Helper traits

Expand Down
21 changes: 21 additions & 0 deletions phpunitpolyfills-autoload.php
Expand Up @@ -105,6 +105,10 @@ public static function load( $className ) {
self::loadAssertObjectEquals();
return true;

case 'Yoast\PHPUnitPolyfills\Polyfills\AssertObjectProperty':
self::loadAssertObjectProperty();
return true;

case 'Yoast\PHPUnitPolyfills\TestCases\TestCase':
self::loadTestCase();
return true;
Expand Down Expand Up @@ -421,6 +425,23 @@ public static function loadAssertObjectEquals() {
require_once __DIR__ . '/src/Polyfills/AssertObjectEquals_Empty.php';
}

/**
* Load the AssertObjectProperty polyfill or an empty trait with the same name
* if a PHPUnit version is used which already contains this functionality.
*
* @return void
*/
public static function loadAssertObjectProperty() {
if ( \method_exists( '\PHPUnit\Framework\Assert', 'assertObjectHasProperty' ) === false ) {
// PHPUnit < 9.6.11.
require_once __DIR__ . '/src/Polyfills/AssertObjectProperty.php';
return;
}

// PHPUnit >= 9.6.11.
require_once __DIR__ . '/src/Polyfills/AssertObjectProperty_Empty.php';
}

/**
* Load the appropriate TestCase class based on the PHPUnit version being used.
*
Expand Down
155 changes: 155 additions & 0 deletions src/Polyfills/AssertObjectProperty.php
@@ -0,0 +1,155 @@
<?php

namespace Yoast\PHPUnitPolyfills\Polyfills;

use PHPUnit\Framework\Assert;
use ReflectionObject;
use TypeError;
use Yoast\PHPUnitPolyfills\Autoload;

/**
* Polyfill the Assert::assertObjectHasProperty() and Assert::assertObjectNotHasProperty() methods,
* which replace the Assert::assertObjectHasAttribute() and Assert::assertObjectNotHasAttribute() methods.
*
* Introduced in PHPUnit 10.1.0 and PHPUnit 9.6.11.
*
* The Assert::assertObjectHasAttribute() and Assert::assertObjectNotHasAttribute() methods
* were deprecated in PHPUnit 9.6.1 and removed in PHPUnit 10.0.0.
*
* @link https://github.com/sebastianbergmann/phpunit/pull/5231
* @link https://github.com/sebastianbergmann/phpunit/issues/5478
*
* @since 1.1.0
*/
trait AssertObjectProperty {

/**
* Asserts that an object has a specified property.
*
* @param string $propertyName The name of the property.
* @param object $object The object on which to check whether the property exists.
* @param string $message Optional failure message to display.
*
* @return void
*
* @throws TypeError When any of the passed arguments do not meet the required type.
*/
final public static function assertObjectHasProperty( $propertyName, $object, $message = '' ) {
/*
* Parameter input validation.
* In PHPUnit this is done via PHP native type declarations. Emulating this for the polyfill,
* including for those PHPUnit versions where we hand off to a native PHPUnit alternative, as
* otherwise the method referenced in the error message would get very confusing and inconsistent.
*/
if ( \is_string( $propertyName ) === false ) {
throw new TypeError(
\sprintf(
'Argument 1 passed to assertObjectHasProperty() must be of type string, %s given',
\gettype( $propertyName )
)
);
}
if ( \is_object( $object ) === false ) {
throw new TypeError(
\sprintf(
'Argument 2 passed to assertObjectHasProperty() must be of type object, %s given',
\gettype( $object )
)
);
}

if ( \method_exists( '\PHPUnit\Framework\Assert', 'assertObjectHasAttribute' )
&& \version_compare( Autoload::getPHPUnitVersion(), '9.6.0', '<=' )
) {
// PHPUnit <= 9.6.0.
static::assertObjectHasAttribute( $propertyName, $object, $message );
return;
}

/*
* PHPUnit 9.6.1+.
* Note: letting this polyfill code kick in for PHPUnit 9.6.1+ as well
* to prevent the PHPUnit deprecation notice showing.
*/
$msg = self::assertObjectHasPropertyFailureDescription( $object );
$msg .= \sprintf( ' has property "%s".', $propertyName );
if ( $message !== '' ) {
$msg = $message . \PHP_EOL . $msg;
}

$hasProperty = ( new ReflectionObject( $object ) )->hasProperty( $propertyName );
static::assertTrue( $hasProperty, $msg );
}

/**
* Asserts that an object does not have a specified property.
*
* @param string $propertyName The name of the property.
* @param object $object The object on which to check whether the property exists.
* @param string $message Optional failure message to display.
*
* @return void
*
* @throws TypeError When any of the passed arguments do not meet the required type.
*/
final public static function assertObjectNotHasProperty( $propertyName, $object, $message = '' ) {
/*
* Parameter input validation.
* In PHPUnit this is done via PHP native type declarations. Emulating this for the polyfill,
* including for those PHPUnit versions where we hand off to a native PHPUnit alternative, as
* otherwise the method referenced in the error message would get very confusing and inconsistent.
*/
if ( \is_string( $propertyName ) === false ) {
throw new TypeError(
\sprintf(
'Argument 1 passed to assertObjectNotHasProperty() must be of type string, %s given',
\gettype( $propertyName )
)
);
}
if ( \is_object( $object ) === false ) {
throw new TypeError(
\sprintf(
'Argument 2 passed to assertObjectNotHasProperty() must be of type object, %s given',
\gettype( $object )
)
);
}

if ( \method_exists( '\PHPUnit\Framework\Assert', 'assertObjectNotHasAttribute' )
&& \version_compare( Autoload::getPHPUnitVersion(), '9.6.0', '<=' )
) {
// PHPUnit <= 9.6.0.
static::assertObjectNotHasAttribute( $propertyName, $object, $message );
return;
}

/*
* PHPUnit 9.6.1+.
* Note: letting this polyfill code kick in for PHPUnit 9.6.1+ as well
* to prevent the PHPUnit deprecation notice showing.
*/
$msg = self::assertObjectHasPropertyFailureDescription( $object );
$msg .= \sprintf( ' does not have property "%s".', $propertyName );
if ( $message !== '' ) {
$msg = $message . \PHP_EOL . $msg;
}

$hasProperty = ( new ReflectionObject( $object ) )->hasProperty( $propertyName );
static::assertFalse( $hasProperty, $msg );
}

/**
* Returns the description of the failure.
*
* @param object $object The object under test.
*
* @return string
*/
private static function assertObjectHasPropertyFailureDescription( $object ) {
return \sprintf(
'Failed asserting that object of class "%s"',
\get_class( $object )
);
}
}
10 changes: 10 additions & 0 deletions src/Polyfills/AssertObjectProperty_Empty.php
@@ -0,0 +1,10 @@
<?php

namespace Yoast\PHPUnitPolyfills\Polyfills;

/**
* Empty trait for use with PHPUnit >= 9.6.11 in which this polyfill is not needed.
*
* @since 1.1.0
*/
trait AssertObjectProperty {}
2 changes: 2 additions & 0 deletions src/TestCases/TestCasePHPUnitGte8.php
Expand Up @@ -8,6 +8,7 @@
use Yoast\PHPUnitPolyfills\Polyfills\AssertFileEqualsSpecializations;
use Yoast\PHPUnitPolyfills\Polyfills\AssertionRenames;
use Yoast\PHPUnitPolyfills\Polyfills\AssertObjectEquals;
use Yoast\PHPUnitPolyfills\Polyfills\AssertObjectProperty;
use Yoast\PHPUnitPolyfills\Polyfills\EqualToSpecializations;
use Yoast\PHPUnitPolyfills\Polyfills\ExpectExceptionMessageMatches;
use Yoast\PHPUnitPolyfills\Polyfills\ExpectPHPException;
Expand All @@ -28,6 +29,7 @@ abstract class TestCase extends PHPUnit_TestCase {
use AssertFileEqualsSpecializations;
use AssertionRenames;
use AssertObjectEquals;
use AssertObjectProperty;
use EqualToSpecializations;
use ExpectExceptionMessageMatches;
use ExpectPHPException;
Expand Down
2 changes: 2 additions & 0 deletions src/TestCases/TestCasePHPUnitLte7.php
Expand Up @@ -12,6 +12,7 @@
use Yoast\PHPUnitPolyfills\Polyfills\AssertIsType;
use Yoast\PHPUnitPolyfills\Polyfills\AssertNumericType;
use Yoast\PHPUnitPolyfills\Polyfills\AssertObjectEquals;
use Yoast\PHPUnitPolyfills\Polyfills\AssertObjectProperty;
use Yoast\PHPUnitPolyfills\Polyfills\AssertStringContains;
use Yoast\PHPUnitPolyfills\Polyfills\EqualToSpecializations;
use Yoast\PHPUnitPolyfills\Polyfills\ExpectException;
Expand Down Expand Up @@ -39,6 +40,7 @@ abstract class TestCase extends PHPUnit_TestCase {
use AssertIsType;
use AssertNumericType;
use AssertObjectEquals;
use AssertObjectProperty;
use AssertStringContains;
use EqualToSpecializations;
use ExpectException;
Expand Down
2 changes: 2 additions & 0 deletions src/TestCases/XTestCase.php
Expand Up @@ -12,6 +12,7 @@
use Yoast\PHPUnitPolyfills\Polyfills\AssertIsType;
use Yoast\PHPUnitPolyfills\Polyfills\AssertNumericType;
use Yoast\PHPUnitPolyfills\Polyfills\AssertObjectEquals;
use Yoast\PHPUnitPolyfills\Polyfills\AssertObjectProperty;
use Yoast\PHPUnitPolyfills\Polyfills\AssertStringContains;
use Yoast\PHPUnitPolyfills\Polyfills\EqualToSpecializations;
use Yoast\PHPUnitPolyfills\Polyfills\ExpectException;
Expand Down Expand Up @@ -41,6 +42,7 @@ abstract class XTestCase extends PHPUnit_TestCase {
use AssertIsType;
use AssertNumericType;
use AssertObjectEquals;
use AssertObjectProperty;
use AssertStringContains;
use EqualToSpecializations;
use ExpectException;
Expand Down

0 comments on commit 2810188

Please sign in to comment.