diff --git a/.phpcs.xml.dist b/.phpcs.xml.dist
index 710fb4c..67c1ea3 100644
--- a/.phpcs.xml.dist
+++ b/.phpcs.xml.dist
@@ -145,4 +145,15 @@
/tests/TestCases/TestCaseTestTrait\.php$
+
+
+ /tests/Polyfills/Fixtures/ChildValueObject\.php$
+ /tests/Polyfills/Fixtures/ValueObject\.php$
+ /tests/Polyfills/Fixtures/ValueObjectUnion\.php$
+
+
+ /tests/Polyfills/Fixtures/ValueObjectUnion\.php$
+ /tests/Polyfills/Fixtures/ValueObjectUnionNoReturnType\.php$
+
+
diff --git a/README.md b/README.md
index 5d870f8..f78a1f6 100644
--- a/README.md
+++ b/README.md
@@ -436,6 +436,24 @@ if ( self::shouldClosedResourceAssertionBeSkipped( $actual ) === false ) {
> :point_right: While this polyfill is tested extensively, testing for these kind of bugs exhaustively is _hard_.
> Please [report any bugs](https://github.com/Yoast/PHPUnit-Polyfills/issues/new/choose) found and include a clear code sample to reproduce the issue.
+#### PHPUnit < 9.4.0: `Yoast\PHPUnitPolyfills\Polyfills\AssertObjectEquals`
+
+Polyfills the [`Assert::assertObjectEquals()`] method to verify two (value) objects are considered equal.
+This assertion expects an object to contain a comparator method in the object itself. This comparator method is subsequently called to verify the "equalness" of the objects.
+
+The `assertObjectEquals() assertion was introduced in PHPUnit 9.4.0.
+
+> :info: Due to [limitations in how this assertion is implemented in PHPUnit] itself, it is currently not possible to create a single comparator method which will be compatible with both PHP < 7.0 and PHP 7.0 or higher.
+>
+> In effect two declarations of the same object would be needed to be compatible with PHP < 7.0 and PHP 7.0 and higher and still allow for testing the object using the `assertObjectEquals()` assertion.
+>
+> Due to this limitation, it is recommended to only use this assertion if the minimum supported PHP version of a project is PHP 7.0 or higher; or if the project does not run its tests on PHPUnit >= 9.4.0.
+
+[limitations in how this assertion is implemented in PHPUnit]: https://github.com/sebastianbergmann/phpunit/issues/4707
+
+
### Helper traits
diff --git a/composer.json b/composer.json
index 9d0e049..f73fffd 100644
--- a/composer.json
+++ b/composer.json
@@ -47,10 +47,10 @@
},
"scripts": {
"lint7": [
- "@php ./vendor/php-parallel-lint/php-parallel-lint/parallel-lint . -e php --exclude vendor --exclude .git --exclude src/Exceptions/Error.php --exclude src/Exceptions/TypeError.php"
+ "@php ./vendor/php-parallel-lint/php-parallel-lint/parallel-lint . -e php --exclude vendor --exclude .git --exclude src/Exceptions/Error.php --exclude src/Exceptions/TypeError.php --exclude tests/Polyfills/Fixtures/ValueObjectUnion.php --exclude tests/Polyfills/Fixtures/ValueObjectUnionNoReturnType.php
],
"lint-lt70": [
- "@php ./vendor/php-parallel-lint/php-parallel-lint/parallel-lint . -e php --exclude vendor --exclude .git --exclude src/TestCases/TestCasePHPUnitGte8.php --exclude src/TestListeners/TestListenerDefaultImplementationPHPUnitGte7.php"
+ "@php ./vendor/php-parallel-lint/php-parallel-lint/parallel-lint . -e php --exclude vendor --exclude .git --exclude src/TestCases/TestCasePHPUnitGte8.php --exclude src/TestListeners/TestListenerDefaultImplementationPHPUnitGte7.php --exclude tests/Polyfills/Fixtures/ChildValueObject.php --exclude tests/Polyfills/Fixtures/ValueObject.php --exclude tests/Polyfills/Fixtures/ValueObjectUnion.php --exclude tests/Polyfills/Fixtures/ValueObjectUnionNoReturnType.php
],
"lint-gte80": [
"@php ./vendor/php-parallel-lint/php-parallel-lint/parallel-lint . -e php --exclude vendor --exclude .git"
diff --git a/phpunitpolyfills-autoload.php b/phpunitpolyfills-autoload.php
index 5376f7a..318ab6a 100644
--- a/phpunitpolyfills-autoload.php
+++ b/phpunitpolyfills-autoload.php
@@ -98,6 +98,10 @@ public static function load( $className ) {
self::loadAssertClosedResource();
return true;
+ case 'Yoast\PHPUnitPolyfills\Polyfills\AssertObjectEquals':
+ self::loadAssertObjectEquals();
+ return true;
+
case 'Yoast\PHPUnitPolyfills\TestCases\TestCase':
self::loadTestCase();
return true;
@@ -108,6 +112,7 @@ public static function load( $className ) {
/*
* Handles:
+ * - Yoast\PHPUnitPolyfills\Exceptions\InvalidComparisonMethodException
* - Yoast\PHPUnitPolyfills\Helpers\AssertAttributeHelper
* - Yoast\PHPUnitPolyfills\Helpers\ResourceHelper
* - Yoast\PHPUnitPolyfills\TestCases\XTestCase
@@ -400,6 +405,23 @@ public static function loadAssertClosedResource() {
require_once __DIR__ . '/src/Polyfills/AssertClosedResource_Empty.php';
}
+ /**
+ * Load the AssertObjectEquals 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 loadAssertObjectEquals() {
+ if ( \method_exists( '\PHPUnit\Framework\Assert', 'assertObjectEquals' ) === false ) {
+ // PHPUnit < 9.4.0.
+ require_once __DIR__ . '/src/Polyfills/AssertObjectEquals.php';
+ return;
+ }
+
+ // PHPUnit >= 9.4.0.
+ require_once __DIR__ . '/src/Polyfills/AssertObjectEquals_Empty.php';
+ }
+
/**
* Load the appropriate TestCase class based on the PHPUnit version being used.
*
diff --git a/src/Exceptions/InvalidComparisonMethodException.php b/src/Exceptions/InvalidComparisonMethodException.php
new file mode 100644
index 0000000..db05e34
--- /dev/null
+++ b/src/Exceptions/InvalidComparisonMethodException.php
@@ -0,0 +1,23 @@
+getMessage() . \PHP_EOL;
+ }
+}
diff --git a/src/Polyfills/AssertObjectEquals.php b/src/Polyfills/AssertObjectEquals.php
new file mode 100644
index 0000000..4e8be06
--- /dev/null
+++ b/src/Polyfills/AssertObjectEquals.php
@@ -0,0 +1,234 @@
+$method($expected)` returns boolean true.
+ *
+ * @param object $expected Expected value.
+ * @param object $actual The value to test.
+ * @param string $method The name of the comparator method within the object.
+ * @param string $message Optional failure message to display.
+ *
+ * @return void
+ *
+ * @throws TypeError When any of the passed arguments do not meet the required type.
+ * @throws InvalidComparisonMethodException When the comparator method does not comply with the requirements.
+ */
+ public static function assertObjectEquals( $expected, $actual, $method = 'equals', $message = '' ) {
+ /*
+ * Parameter input validation.
+ * In PHPUnit this is done via PHP native type declarations. Emulating this for the polyfill.
+ */
+ if ( \is_object( $expected ) === false ) {
+ throw new TypeError(
+ \sprintf(
+ 'Argument 1 passed to assertObjectEquals() must be an object, %s given',
+ \gettype( $expected )
+ );
+ );
+ }
+
+ if ( \is_object( $actual ) === false ) {
+ throw new TypeError(
+ \sprintf(
+ 'Argument 2 passed to assertObjectEquals() must be an object, %s given',
+ \gettype( $actual )
+ );
+ );
+ }
+
+ if ( \is_scalar( $method ) === false ) {
+ throw new TypeError(
+ \sprintf(
+ 'Argument 3 passed to assertObjectEquals() must be of the type string, %s given',
+ \gettype( $method )
+ );
+ );
+ }
+ else {
+ $method = (string) $method;
+ }
+
+ /*
+ * Comparator method validation.
+ */
+ $reflObject = new ReflectionObject( $actual );
+
+ if ( $reflObject->hasMethod( $method ) === false ) {
+ throw new InvalidComparisonMethodException(
+ \sprintf(
+ 'Comparison method %s::%s() does not exist.',
+ \get_class( $actual ),
+ $method
+ )
+ );
+ }
+
+ $reflMethod = $reflObject->getMethod( $method );
+
+ /*
+ * As the next step, PHPUnit natively would validate the return type,
+ * but as return type declarations is a PHP 7.0+ feature, the polyfill
+ * skips this check in favour of checking the type of the actual
+ * returned value.
+ *
+ * Also see the upstream discussion about this:
+ * {@link https://github.com/sebastianbergmann/phpunit/issues/4707}
+ */
+
+ /*
+ * Comparator method parameter requirements validation.
+ */
+ if ( $reflMethod->getNumberOfParameters() !== 1
+ || $reflMethod->getNumberOfRequiredParameters() !== 1
+ ) {
+ throw new InvalidComparisonMethodException(
+ \sprintf(
+ 'Comparison method %s::%s() does not declare exactly one parameter.',
+ \get_class( $actual ),
+ $method
+ )
+ );
+ }
+
+ $noDeclaredTypeError = \sprintf(
+ 'Parameter of comparison method %s::%s() does not have a declared type.',
+ \get_class( $actual ),
+ $method
+ );
+
+ $notAcceptableTypeError = \sprintf(
+ '%s is not an accepted argument type for comparison method %s::%s().',
+ \get_class( $expected ),
+ \get_class( $actual ),
+ $method
+ );
+
+ $reflParameter = $reflMethod->getParameters()[0];
+
+ if ( \method_exists( $reflParameter, 'hasType' ) ) {
+ // PHP >= 7.0.
+ $hasType = $reflParameter->hasType();
+ if ( $hasType === false ) {
+ throw new InvalidComparisonMethodException( $noDeclaredTypeError );
+ }
+
+ $type = $reflParameter->getType();
+ if ( \class_exists( 'ReflectionNamedType' ) ) {
+ // PHP >= 7.1.
+ if ( ( $type instanceof ReflectionNamedType ) === false ) {
+ throw new InvalidComparisonMethodException( $noDeclaredTypeError );
+ }
+
+ $typeName = $type->getName();
+ }
+ else {
+ /*
+ * PHP 7.0.
+ * Checking for `ReflectionType` will not throw an error on union types,
+ * but then again union types are not supported on PHP 7.0.
+ */
+ if ( ( $type instanceof ReflectionType ) === false ) {
+ throw new InvalidComparisonMethodException( $noDeclaredTypeError );
+ }
+
+ $typeName = (string) $type;
+ }
+ }
+ else {
+ // PHP < 7.0.
+ try {
+ /*
+ * Using `ReflectionParameter::getClass()` will trigger an autoload of the class,
+ * but that's okay as for a valid class type that would be triggered on the
+ * function call to the $method (at the end of this assertion) anyway.
+ */
+ $hasType = $reflParameter->getClass();
+ } catch ( ReflectionException $e ) {
+ // Class with a type declaration for a non-existent class.
+ throw new InvalidComparisonMethodException( $notAcceptableTypeError );
+ }
+
+ if ( ( $hasType instanceof ReflectionClass ) === false ) {
+ // Array or callable type.
+ throw new InvalidComparisonMethodException( $noDeclaredTypeError );
+ }
+
+ $typeName = $hasType->name;
+ }
+
+ /*
+ * Validate that the $expected object complies with the declared parameter type.
+ */
+ if ( $typeName === 'self' ) {
+ $typeName = \get_class( $actual );
+ }
+
+ if ( ( $expected instanceof $typeName ) === false ) {
+ throw new InvalidComparisonMethodException( $notAcceptableTypeError );
+ }
+
+ /*
+ * Execute the comparator method.
+ */
+ $result = $actual->{$method}( $expected );
+
+ if ( \is_bool( $result ) === false ) {
+ throw new InvalidComparisonMethodException(
+ \sprintf(
+ '%s::%s() does not return a boolean value.',
+ \get_class( $actual ),
+ $method
+ )
+ );
+ }
+
+ $msg = \sprintf(
+ 'Failed asserting that two objects are equal. The objects are not equal according to %s::%s()',
+ \get_class( $actual ),
+ $method
+ );
+
+ if ( $message !== '' ) {
+ $msg = $message . \PHP_EOL . $msg;
+ }
+
+ static::assertTrue( $result, $msg );
+ }
+}
diff --git a/src/Polyfills/AssertObjectEquals_Empty.php b/src/Polyfills/AssertObjectEquals_Empty.php
new file mode 100644
index 0000000..4390a97
--- /dev/null
+++ b/src/Polyfills/AssertObjectEquals_Empty.php
@@ -0,0 +1,8 @@
+= 9.4.0 in which this polyfill is not needed.
+ */
+trait AssertObjectEquals {}
diff --git a/src/TestCases/TestCasePHPUnitGte8.php b/src/TestCases/TestCasePHPUnitGte8.php
index f819be9..cfe397c 100644
--- a/src/TestCases/TestCasePHPUnitGte8.php
+++ b/src/TestCases/TestCasePHPUnitGte8.php
@@ -7,6 +7,7 @@
use Yoast\PHPUnitPolyfills\Polyfills\AssertClosedResource;
use Yoast\PHPUnitPolyfills\Polyfills\AssertFileEqualsSpecializations;
use Yoast\PHPUnitPolyfills\Polyfills\AssertionRenames;
+use Yoast\PHPUnitPolyfills\Polyfills\AssertObjectEquals;
use Yoast\PHPUnitPolyfills\Polyfills\EqualToSpecializations;
use Yoast\PHPUnitPolyfills\Polyfills\ExpectExceptionMessageMatches;
use Yoast\PHPUnitPolyfills\Polyfills\ExpectPHPException;
@@ -26,6 +27,7 @@ abstract class TestCase extends PHPUnit_TestCase {
use AssertClosedResource;
use AssertFileEqualsSpecializations;
use AssertionRenames;
+ use AssertObjectEquals;
use EqualToSpecializations;
use ExpectExceptionMessageMatches;
use ExpectPHPException;
diff --git a/src/TestCases/TestCasePHPUnitLte7.php b/src/TestCases/TestCasePHPUnitLte7.php
index e906972..34cf29b 100644
--- a/src/TestCases/TestCasePHPUnitLte7.php
+++ b/src/TestCases/TestCasePHPUnitLte7.php
@@ -11,6 +11,7 @@
use Yoast\PHPUnitPolyfills\Polyfills\AssertionRenames;
use Yoast\PHPUnitPolyfills\Polyfills\AssertIsType;
use Yoast\PHPUnitPolyfills\Polyfills\AssertNumericType;
+use Yoast\PHPUnitPolyfills\Polyfills\AssertObjectEquals;
use Yoast\PHPUnitPolyfills\Polyfills\AssertStringContains;
use Yoast\PHPUnitPolyfills\Polyfills\EqualToSpecializations;
use Yoast\PHPUnitPolyfills\Polyfills\ExpectException;
@@ -37,6 +38,7 @@ abstract class TestCase extends PHPUnit_TestCase {
use AssertionRenames;
use AssertIsType;
use AssertNumericType;
+ use AssertObjectEquals;
use AssertStringContains;
use EqualToSpecializations;
use ExpectException;
diff --git a/src/TestCases/XTestCase.php b/src/TestCases/XTestCase.php
index 3d4dbd9..4db9148 100644
--- a/src/TestCases/XTestCase.php
+++ b/src/TestCases/XTestCase.php
@@ -11,6 +11,7 @@
use Yoast\PHPUnitPolyfills\Polyfills\AssertionRenames;
use Yoast\PHPUnitPolyfills\Polyfills\AssertIsType;
use Yoast\PHPUnitPolyfills\Polyfills\AssertNumericType;
+use Yoast\PHPUnitPolyfills\Polyfills\AssertObjectEquals;
use Yoast\PHPUnitPolyfills\Polyfills\AssertStringContains;
use Yoast\PHPUnitPolyfills\Polyfills\EqualToSpecializations;
use Yoast\PHPUnitPolyfills\Polyfills\ExpectException;
@@ -39,6 +40,7 @@ abstract class XTestCase extends PHPUnit_TestCase {
use AssertionRenames;
use AssertIsType;
use AssertNumericType;
+ use AssertObjectEquals;
use AssertStringContains;
use EqualToSpecializations;
use ExpectException;
diff --git a/tests/Polyfills/AssertObjectEqualsPHPUnitLt940Test.php b/tests/Polyfills/AssertObjectEqualsPHPUnitLt940Test.php
new file mode 100644
index 0000000..90a0e33
--- /dev/null
+++ b/tests/Polyfills/AssertObjectEqualsPHPUnitLt940Test.php
@@ -0,0 +1,314 @@
+=' ) ) {
+ $this->markTestSkipped( 'This test can not be run with the PHPUnit native implementation of assertObjectEquals()' );
+ }
+ }
+
+ /**
+ * Verify availability of the assertObjectEquals() method.
+ *
+ * @return void
+ */
+ public function testAssertObjectEquals() {
+ $expected = new ValueObjectNoReturnType( 'test' );
+ $actual = new ValueObjectNoReturnType( 'test' );
+ $this->assertObjectEquals( $expected, $actual );
+ }
+
+ /**
+ * Verify behaviour when passing the $method parameter.
+ *
+ * @return void
+ */
+ public function testAssertObjectEqualsCustomMethodName() {
+ $expected = new ValueObjectNoReturnType( 'different name' );
+ $actual = new ValueObjectNoReturnType( 'different name' );
+ $this->assertObjectEquals( $expected, $actual, 'nonDefaultName' );
+ }
+
+ /**
+ * Verify that the assertObjectEquals() method throws an error when the $expected parameter is not an object.
+ *
+ * @return void
+ */
+ public function testAssertObjectEqualsFailsOnExpectedNotObject() {
+ $pattern = '`^Argument 1 passed to [^\s]*assertObjectEquals\(\) must be an object, string given`';
+
+ $this->expectException( 'TypeError' );
+ $this->expectExceptionMessageMatches( $pattern );
+
+ $actual = new ValueObjectNoReturnType( 'test' );
+ $this->assertObjectEquals( 'className', $actual );
+ }
+
+ /**
+ * Verify that the assertObjectEquals() method throws an error when the $actual parameter is not an object.
+ *
+ * @return void
+ */
+ public function testAssertObjectEqualsFailsOnActualNotObject() {
+ $pattern = '`^Argument 2 passed to [^\s]*assertObjectEquals\(\) must be an object, string given`';
+
+ $this->expectException( 'TypeError' );
+ $this->expectExceptionMessageMatches( $pattern );
+
+ $expected = new ValueObjectNoReturnType( 'test' );
+ $this->assertObjectEquals( $expected, 'className' );
+ }
+
+ /**
+ * Verify that the assertObjectEquals() method throws an error when the $method parameter is not
+ * juggleable to a string.
+ *
+ * @return void
+ */
+ public function testAssertObjectEqualsFailsOnMethodNotJuggleableToString() {
+ $pattern = '`^Argument 3 passed to [^\s]*assertObjectEquals\(\) must be of the type string, array given`';
+
+ $this->expectException( 'TypeError' );
+ $this->expectExceptionMessageMatches( $pattern );
+
+ $expected = new ValueObjectNoReturnType( 'test' );
+ $actual = new ValueObjectNoReturnType( 'test' );
+ $this->assertObjectEquals( $expected, $actual, [] );
+ }
+
+ /**
+ * Verify that the assertObjectEquals() method throws an error when the $actual object
+ * does not contain a method called $method.
+ *
+ * @return void
+ */
+ public function testAssertObjectEqualsFailsOnMethodNotDeclared() {
+ $msg = 'Comparison method Yoast\PHPUnitPolyfills\Tests\Polyfills\Fixtures\ValueObjectNoReturnType::doesNotExist() does not exist.';
+
+ $this->expectException( self::COMPARATOR_EXCEPTION );
+ $this->expectExceptionMessage( $msg );
+
+ $expected = new ValueObjectNoReturnType( 'test' );
+ $actual = new ValueObjectNoReturnType( 'test' );
+ $this->assertObjectEquals( $expected, $actual, 'doesNotExist' );
+ }
+
+ /**
+ * Verify that the assertObjectEquals() method throws an error when the $method accepts more than one parameter.
+ *
+ * @return void
+ */
+ public function testAssertObjectEqualsFailsOnMethodAllowsForMoreParams() {
+ $msg = 'Comparison method Yoast\PHPUnitPolyfills\Tests\Polyfills\Fixtures\ValueObjectNoReturnType::equalsTwoParams() does not declare exactly one parameter.';
+
+ $this->expectException( self::COMPARATOR_EXCEPTION );
+ $this->expectExceptionMessage( $msg );
+
+ $expected = new ValueObjectNoReturnType( 'test' );
+ $actual = new ValueObjectNoReturnType( 'test' );
+ $this->assertObjectEquals( $expected, $actual, 'equalsTwoParams' );
+ }
+
+ /**
+ * Verify that the assertObjectEquals() method throws an error when the $method is not a required parameter.
+ *
+ * @return void
+ */
+ public function testAssertObjectEqualsFailsOnMethodParamNotRequired() {
+ $msg = 'Comparison method Yoast\PHPUnitPolyfills\Tests\Polyfills\Fixtures\ValueObjectNoReturnType::equalsParamNotRequired() does not declare exactly one parameter.';
+
+ $this->expectException( self::COMPARATOR_EXCEPTION );
+ $this->expectExceptionMessage( $msg );
+
+ $expected = new ValueObjectNoReturnType( 'test' );
+ $actual = new ValueObjectNoReturnType( 'test' );
+ $this->assertObjectEquals( $expected, $actual, 'equalsParamNotRequired' );
+ }
+
+ /**
+ * Verify that the assertObjectEquals() method throws an error when the $method parameter
+ * does not have a type declaration.
+ *
+ * @return void
+ */
+ public function testAssertObjectEqualsFailsOnMethodParamMissingTypeDeclaration() {
+ $msg = 'Parameter of comparison method Yoast\PHPUnitPolyfills\Tests\Polyfills\Fixtures\ValueObjectNoReturnType::equalsParamNoType() does not have a declared type.';
+
+ $this->expectException( self::COMPARATOR_EXCEPTION );
+ $this->expectExceptionMessage( $msg );
+
+ $expected = new ValueObjectNoReturnType( 'test' );
+ $actual = new ValueObjectNoReturnType( 'test' );
+ $this->assertObjectEquals( $expected, $actual, 'equalsParamNoType' );
+ }
+
+ /**
+ * Verify that the assertObjectEquals() method throws an error when the $method parameter
+ * has a PHP 8.0+ union type declaration.
+ *
+ * @requires PHP 8.0
+ *
+ * @return void
+ */
+ public function testAssertObjectEqualsFailsOnMethodParamHasUnionTypeDeclaration() {
+ $msg = 'Parameter of comparison method Yoast\PHPUnitPolyfills\Tests\Polyfills\Fixtures\ValueObjectUnionNoReturnType::equalsParamUnionType() does not have a declared type.';
+
+ $this->expectException( self::COMPARATOR_EXCEPTION );
+ $this->expectExceptionMessage( $msg );
+
+ $expected = new ValueObjectUnionNoReturnType( 'test' );
+ $actual = new ValueObjectUnionNoReturnType( 'test' );
+ $this->assertObjectEquals( $expected, $actual, 'equalsParamUnionType' );
+ }
+
+ /**
+ * Verify that the assertObjectEquals() method throws an error when the $method parameter
+ * does not have a class-based type declaration.
+ *
+ * @return void
+ */
+ public function testAssertObjectEqualsFailsOnMethodParamNonClassTypeDeclaration() {
+ $msg = 'is not an accepted argument type for comparison method Yoast\PHPUnitPolyfills\Tests\Polyfills\Fixtures\ValueObjectNoReturnType::equalsParamNonClassType().';
+ if ( \PHP_VERSION_ID < 70000 ) {
+ $msg = 'Parameter of comparison method Yoast\PHPUnitPolyfills\Tests\Polyfills\Fixtures\ValueObjectNoReturnType::equalsParamNonClassType() does not have a declared type.';
+ }
+
+ $this->expectException( self::COMPARATOR_EXCEPTION );
+ $this->expectExceptionMessage( $msg );
+
+ $expected = new ValueObjectNoReturnType( 'test' );
+ $actual = new ValueObjectNoReturnType( 'test' );
+ $this->assertObjectEquals( $expected, $actual, 'equalsParamNonClassType' );
+ }
+
+ /**
+ * Verify that the assertObjectEquals() method throws an error when the $method parameter
+ * has a class-based type declaration, but for a class which doesn't exist.
+ *
+ * @return void
+ */
+ public function testAssertObjectEqualsFailsOnMethodParamNonExistentClassTypeDeclaration() {
+ $msg = 'is not an accepted argument type for comparison method Yoast\PHPUnitPolyfills\Tests\Polyfills\Fixtures\ValueObjectNoReturnType::equalsParamNonExistentClassType().';
+
+ $this->expectException( self::COMPARATOR_EXCEPTION );
+ $this->expectExceptionMessage( $msg );
+
+ $expected = new ValueObjectNoReturnType( 'test' );
+ $actual = new ValueObjectNoReturnType( 'test' );
+ $this->assertObjectEquals( $expected, $actual, 'equalsParamNonExistentClassType' );
+ }
+
+ /**
+ * Verify that the assertObjectEquals() method throws an error when $expected is not
+ * an instance of the type declared for the $method parameter.
+ *
+ * @return void
+ */
+ public function testAssertObjectEqualsFailsOnMethodParamTypeMismatch() {
+ $msg = 'is not an accepted argument type for comparison method Yoast\PHPUnitPolyfills\Tests\Polyfills\Fixtures\ValueObjectNoReturnType::equals().';
+
+ $this->expectException( self::COMPARATOR_EXCEPTION );
+ $this->expectExceptionMessage( $msg );
+
+ $actual = new ValueObjectNoReturnType( 'test' );
+ $this->assertObjectEquals( new stdClass(), $actual );
+ }
+
+ /**
+ * Verify that the assertObjectEquals() method fails a test when a call to method
+ * determines that the objects are not equal.
+ *
+ * @return void
+ */
+ public function testAssertObjectEqualsFailsAsNotEqual() {
+ $msg = 'Failed asserting that two objects are equal.';
+
+ $this->expectException( $this->getAssertionFailedExceptionName() );
+ $this->expectExceptionMessage( $msg );
+
+ $expected = new ValueObjectNoReturnType( 'test' );
+ $actual = new ValueObjectNoReturnType( 'testing... 1..2..3' );
+ $this->assertObjectEquals( $expected, $actual );
+ }
+
+ /**
+ * Verify that the assertObjectEquals() method fails a test with a custom failure message, when a call
+ * to the method determines that the objects are not equal and the custom $message parameter has been passed.
+ *
+ * @return void
+ */
+ public function testAssertObjectEqualsFailsAsNotEqualWithCustomMessage() {
+ $pattern = '`^This assertion failed for reason XYZ\s+Failed asserting that two objects are equal\.`';
+
+ $this->expectException( $this->getAssertionFailedExceptionName() );
+ $this->expectExceptionMessageMatches( $pattern );
+
+ $expected = new ValueObjectNoReturnType( 'test' );
+ $actual = new ValueObjectNoReturnType( 'testing... 1..2..3' );
+ $this->assertObjectEquals( $expected, $actual, 'equals', 'This assertion failed for reason XYZ' );
+ }
+
+ /**
+ * Helper function: retrieve the name of the "assertion failed" exception to expect (PHPUnit cross-version).
+ *
+ * @return string
+ */
+ public function getAssertionFailedExceptionName() {
+ $exception = 'PHPUnit\Framework\AssertionFailedError';
+ if ( \class_exists( 'PHPUnit_Framework_AssertionFailedError' ) ) {
+ // PHPUnit < 6.
+ $exception = 'PHPUnit_Framework_AssertionFailedError';
+ }
+
+ return $exception;
+ }
+}
diff --git a/tests/Polyfills/AssertObjectEqualsTest.php b/tests/Polyfills/AssertObjectEqualsTest.php
new file mode 100644
index 0000000..384a62a
--- /dev/null
+++ b/tests/Polyfills/AssertObjectEqualsTest.php
@@ -0,0 +1,391 @@
+assertObjectEquals( $expected, $actual );
+ }
+
+ /**
+ * Verify behaviour when passing the $method parameter.
+ *
+ * @return void
+ */
+ public function testAssertObjectEqualsCustomMethodName() {
+ $expected = new ValueObject( 'different name' );
+ $actual = new ValueObject( 'different name' );
+ $this->assertObjectEquals( $expected, $actual, 'nonDefaultName' );
+ }
+
+ /**
+ * Verify behaviour when $expected is a child of $actual.
+ *
+ * @return void
+ */
+ public function testAssertObjectEqualsExpectedChildOfActual() {
+ $expected = new ChildValueObject( 'inheritance' );
+ $actual = new ValueObject( 'inheritance' );
+ $this->assertObjectEquals( $expected, $actual );
+ }
+
+ /**
+ * Verify behaviour when $actual is a child of $expected.
+ *
+ * @return void
+ */
+ public function testAssertObjectEqualsActualChildOfExpected() {
+ $expected = new ValueObject( 'inheritance' );
+ $actual = new ChildValueObject( 'inheritance' );
+ $this->assertObjectEquals( $expected, $actual );
+ }
+
+ /**
+ * Verify that the assertObjectEquals() method throws an error when the $expected parameter is not an object.
+ *
+ * @return void
+ */
+ public function testAssertObjectEqualsFailsOnExpectedNotObject() {
+ $this->expectException( 'TypeError' );
+
+ if ( \PHP_VERSION_ID >= 80000
+ && \version_compare( PHPUnit_Version::id(), '9.4.0', '>=' )
+ ) {
+ $msg = 'assertObjectEquals(): Argument #1 ($expected) must be of type object, string given';
+ $this->expectExceptionMessage( $msg );
+ }
+ else {
+ // PHP 5/7 or PHP 8 with the polyfill.
+ $pattern = '`^Argument 1 passed to [^\s]*assertObjectEquals\(\) must be an object, string given`';
+ $this->expectExceptionMessageMatches( $pattern );
+ }
+
+ $actual = new ValueObject( 'test' );
+ $this->assertObjectEquals( 'className', $actual );
+ }
+
+ /**
+ * Verify that the assertObjectEquals() method throws an error when the $actual parameter is not an object.
+ *
+ * @return void
+ */
+ public function testAssertObjectEqualsFailsOnActualNotObject() {
+ $this->expectException( 'TypeError' );
+
+ if ( \PHP_VERSION_ID >= 80000
+ && \version_compare( PHPUnit_Version::id(), '9.4.0', '>=' )
+ ) {
+ $msg = 'assertObjectEquals(): Argument #2 ($actual) must be of type object, string given';
+ $this->expectExceptionMessage( $msg );
+ }
+ else {
+ // PHP 5/7.
+ $pattern = '`^Argument 2 passed to [^\s]*assertObjectEquals\(\) must be an object, string given`';
+ $this->expectExceptionMessageMatches( $pattern );
+ }
+
+ $expected = new ValueObject( 'test' );
+ $this->assertObjectEquals( $expected, 'className' );
+ }
+
+ /**
+ * Verify that the assertObjectEquals() method throws an error when the $method parameter is not
+ * juggleable to a string.
+ *
+ * @return void
+ */
+ public function testAssertObjectEqualsFailsOnMethodNotJuggleableToString() {
+ $this->expectException( 'TypeError' );
+
+ if ( \PHP_VERSION_ID >= 80000
+ && \version_compare( PHPUnit_Version::id(), '9.4.0', '>=' )
+ ) {
+ $msg = 'assertObjectEquals(): Argument #3 ($method) must be of type string, array given';
+ $this->expectExceptionMessage( $msg );
+ }
+ else {
+ // PHP 5/7.
+ $pattern = '`^Argument 3 passed to [^\s]*assertObjectEquals\(\) must be of the type string, array given`';
+ $this->expectExceptionMessageMatches( $pattern );
+ }
+
+ $expected = new ValueObject( 'test' );
+ $actual = new ValueObject( 'test' );
+ $this->assertObjectEquals( $expected, $actual, [] );
+ }
+
+ /**
+ * Verify that the assertObjectEquals() method throws an error when the $actual object
+ * does not contain a method called $method.
+ *
+ * @return void
+ */
+ public function testAssertObjectEqualsFailsOnMethodNotDeclared() {
+ $msg = 'Comparison method Yoast\PHPUnitPolyfills\Tests\Polyfills\Fixtures\ValueObject::doesNotExist() does not exist.';
+
+ $exception = self::COMPARATOR_EXCEPTION;
+ if ( \class_exists( 'PHPUnit\Framework\ComparisonMethodDoesNotExistException' ) ) {
+ // PHPUnit > 9.4.0.
+ $exception = 'PHPUnit\Framework\ComparisonMethodDoesNotExistException';
+ }
+
+ $this->expectException( $exception );
+ $this->expectExceptionMessage( $msg );
+
+ $expected = new ValueObject( 'test' );
+ $actual = new ValueObject( 'test' );
+ $this->assertObjectEquals( $expected, $actual, 'doesNotExist' );
+ }
+
+ /**
+ * Verify that the assertObjectEquals() method throws an error when the $method accepts more than one parameter.
+ *
+ * @return void
+ */
+ public function testAssertObjectEqualsFailsOnMethodAllowsForMoreParams() {
+ $msg = 'Comparison method Yoast\PHPUnitPolyfills\Tests\Polyfills\Fixtures\ValueObject::equalsTwoParams() does not declare exactly one parameter.';
+
+ $exception = self::COMPARATOR_EXCEPTION;
+ if ( \class_exists( 'PHPUnit\Framework\ComparisonMethodDoesNotDeclareExactlyOneParameterException' ) ) {
+ // PHPUnit > 9.4.0.
+ $exception = 'PHPUnit\Framework\ComparisonMethodDoesNotDeclareExactlyOneParameterException';
+ }
+
+ $this->expectException( $exception );
+ $this->expectExceptionMessage( $msg );
+
+ $expected = new ValueObject( 'test' );
+ $actual = new ValueObject( 'test' );
+ $this->assertObjectEquals( $expected, $actual, 'equalsTwoParams' );
+ }
+
+ /**
+ * Verify that the assertObjectEquals() method throws an error when the $method is not a required parameter.
+ *
+ * @return void
+ */
+ public function testAssertObjectEqualsFailsOnMethodParamNotRequired() {
+ $msg = 'Comparison method Yoast\PHPUnitPolyfills\Tests\Polyfills\Fixtures\ValueObject::equalsParamNotRequired() does not declare exactly one parameter.';
+
+ $exception = self::COMPARATOR_EXCEPTION;
+ if ( \class_exists( 'PHPUnit\Framework\ComparisonMethodDoesNotDeclareExactlyOneParameterException' ) ) {
+ // PHPUnit > 9.4.0.
+ $exception = 'PHPUnit\Framework\ComparisonMethodDoesNotDeclareExactlyOneParameterException';
+ }
+
+ $this->expectException( $exception );
+ $this->expectExceptionMessage( $msg );
+
+ $expected = new ValueObject( 'test' );
+ $actual = new ValueObject( 'test' );
+ $this->assertObjectEquals( $expected, $actual, 'equalsParamNotRequired' );
+ }
+
+ /**
+ * Verify that the assertObjectEquals() method throws an error when the $method parameter
+ * does not have a type declaration.
+ *
+ * @return void
+ */
+ public function testAssertObjectEqualsFailsOnMethodParamMissingTypeDeclaration() {
+ $msg = 'Parameter of comparison method Yoast\PHPUnitPolyfills\Tests\Polyfills\Fixtures\ValueObject::equalsParamNoType() does not have a declared type.';
+
+ $exception = self::COMPARATOR_EXCEPTION;
+ if ( \class_exists( 'PHPUnit\Framework\ComparisonMethodDoesNotDeclareParameterTypeException' ) ) {
+ // PHPUnit > 9.4.0.
+ $exception = 'PHPUnit\Framework\ComparisonMethodDoesNotDeclareParameterTypeException';
+ }
+
+ $this->expectException( $exception );
+ $this->expectExceptionMessage( $msg );
+
+ $expected = new ValueObject( 'test' );
+ $actual = new ValueObject( 'test' );
+ $this->assertObjectEquals( $expected, $actual, 'equalsParamNoType' );
+ }
+
+ /**
+ * Verify that the assertObjectEquals() method throws an error when the $method parameter
+ * has a PHP 8.0+ union type declaration.
+ *
+ * @requires PHP 8.0
+ *
+ * @return void
+ */
+ public function testAssertObjectEqualsFailsOnMethodParamHasUnionTypeDeclaration() {
+ $msg = 'Parameter of comparison method Yoast\PHPUnitPolyfills\Tests\Polyfills\Fixtures\ValueObjectUnion::equalsParamUnionType() does not have a declared type.';
+
+ $exception = self::COMPARATOR_EXCEPTION;
+ if ( \class_exists( 'PHPUnit\Framework\ComparisonMethodDoesNotDeclareParameterTypeException' ) ) {
+ // PHPUnit > 9.4.0.
+ $exception = 'PHPUnit\Framework\ComparisonMethodDoesNotDeclareParameterTypeException';
+ }
+
+ $this->expectException( $exception );
+ $this->expectExceptionMessage( $msg );
+
+ $expected = new ValueObjectUnion( 'test' );
+ $actual = new ValueObjectUnion( 'test' );
+ $this->assertObjectEquals( $expected, $actual, 'equalsParamUnionType' );
+ }
+
+ /**
+ * Verify that the assertObjectEquals() method throws an error when the $method parameter
+ * does not have a class-based type declaration.
+ *
+ * @return void
+ */
+ public function testAssertObjectEqualsFailsOnMethodParamNonClassTypeDeclaration() {
+ $msg = 'is not an accepted argument type for comparison method Yoast\PHPUnitPolyfills\Tests\Polyfills\Fixtures\ValueObject::equalsParamNonClassType().';
+
+ $exception = self::COMPARATOR_EXCEPTION;
+ if ( \class_exists( 'PHPUnit\Framework\ComparisonMethodDoesNotAcceptParameterTypeException' ) ) {
+ // PHPUnit > 9.4.0.
+ $exception = 'PHPUnit\Framework\ComparisonMethodDoesNotAcceptParameterTypeException';
+ }
+
+ $this->expectException( $exception );
+ $this->expectExceptionMessage( $msg );
+
+ $expected = new ValueObject( 'test' );
+ $actual = new ValueObject( 'test' );
+ $this->assertObjectEquals( $expected, $actual, 'equalsParamNonClassType' );
+ }
+
+ /**
+ * Verify that the assertObjectEquals() method throws an error when the $method parameter
+ * has a class-based type declaration, but for a class which doesn't exist.
+ *
+ * @return void
+ */
+ public function testAssertObjectEqualsFailsOnMethodParamNonExistentClassTypeDeclaration() {
+ $msg = 'is not an accepted argument type for comparison method Yoast\PHPUnitPolyfills\Tests\Polyfills\Fixtures\ValueObject::equalsParamNonExistentClassType().';
+
+ $exception = self::COMPARATOR_EXCEPTION;
+ if ( \class_exists( 'PHPUnit\Framework\ComparisonMethodDoesNotAcceptParameterTypeException' ) ) {
+ // PHPUnit > 9.4.0.
+ $exception = 'PHPUnit\Framework\ComparisonMethodDoesNotAcceptParameterTypeException';
+ }
+
+ $this->expectException( $exception );
+ $this->expectExceptionMessage( $msg );
+
+ $expected = new ValueObject( 'test' );
+ $actual = new ValueObject( 'test' );
+ $this->assertObjectEquals( $expected, $actual, 'equalsParamNonExistentClassType' );
+ }
+
+ /**
+ * Verify that the assertObjectEquals() method throws an error when $expected is not
+ * an instance of the type declared for the $method parameter.
+ *
+ * @return void
+ */
+ public function testAssertObjectEqualsFailsOnMethodParamTypeMismatch() {
+ $msg = 'is not an accepted argument type for comparison method Yoast\PHPUnitPolyfills\Tests\Polyfills\Fixtures\ValueObject::equals().';
+
+ $exception = self::COMPARATOR_EXCEPTION;
+ if ( \class_exists( 'PHPUnit\Framework\ComparisonMethodDoesNotAcceptParameterTypeException' ) ) {
+ // PHPUnit > 9.4.0.
+ $exception = 'PHPUnit\Framework\ComparisonMethodDoesNotAcceptParameterTypeException';
+ }
+
+ $this->expectException( $exception );
+ $this->expectExceptionMessage( $msg );
+
+ $actual = new ValueObject( 'test' );
+ $this->assertObjectEquals( new stdClass(), $actual );
+ }
+
+ /**
+ * Verify that the assertObjectEquals() method fails a test when a call to method
+ * determines that the objects are not equal.
+ *
+ * @return void
+ */
+ public function testAssertObjectEqualsFailsAsNotEqual() {
+ $msg = 'Failed asserting that two objects are equal.';
+
+ $this->expectException( $this->getAssertionFailedExceptionName() );
+ $this->expectExceptionMessage( $msg );
+
+ $expected = new ValueObject( 'test' );
+ $actual = new ValueObject( 'testing... 1..2..3' );
+ $this->assertObjectEquals( $expected, $actual );
+ }
+
+ /**
+ * Verify that the assertObjectEquals() method fails a test with a custom failure message, when a call
+ * to the method determines that the objects are not equal and the custom $message parameter has been passed.
+ *
+ * @return void
+ */
+ public function testAssertObjectEqualsFailsAsNotEqualWithCustomMessage() {
+ $pattern = '`^This assertion failed for reason XYZ\s+Failed asserting that two objects are equal\.`';
+
+ $this->expectException( $this->getAssertionFailedExceptionName() );
+ $this->expectExceptionMessageMatches( $pattern );
+
+ $expected = new ValueObject( 'test' );
+ $actual = new ValueObject( 'testing... 1..2..3' );
+ $this->assertObjectEquals( $expected, $actual, 'equals', 'This assertion failed for reason XYZ' );
+ }
+
+ /**
+ * Helper function: retrieve the name of the "assertion failed" exception to expect (PHPUnit cross-version).
+ *
+ * @return string
+ */
+ public function getAssertionFailedExceptionName() {
+ $exception = 'PHPUnit\Framework\AssertionFailedError';
+ if ( \class_exists( 'PHPUnit_Framework_AssertionFailedError' ) ) {
+ // PHPUnit < 6.
+ $exception = 'PHPUnit_Framework_AssertionFailedError';
+ }
+
+ return $exception;
+ }
+}
diff --git a/tests/Polyfills/Fixtures/ChildValueObject.php b/tests/Polyfills/Fixtures/ChildValueObject.php
new file mode 100644
index 0000000..6eaafed
--- /dev/null
+++ b/tests/Polyfills/Fixtures/ChildValueObject.php
@@ -0,0 +1,36 @@
+value = $value;
+ }
+
+ /**
+ * Comparator method: correctly declared.
+ *
+ * @param ValueObject $other Object to compare.
+ *
+ * @return bool
+ */
+ public function equals( ValueObject $other ): bool {
+ return ( $this->value === $other->value );
+ }
+}
diff --git a/tests/Polyfills/Fixtures/ValueObject.php b/tests/Polyfills/Fixtures/ValueObject.php
new file mode 100644
index 0000000..e2305dd
--- /dev/null
+++ b/tests/Polyfills/Fixtures/ValueObject.php
@@ -0,0 +1,105 @@
+value = $value;
+ }
+
+ /**
+ * Comparator method: correctly declared.
+ *
+ * @param self $other Object to compare.
+ *
+ * @return bool
+ */
+ public function equals( self $other ): bool {
+ return ( $this->value === $other->value );
+ }
+
+ /**
+ * Comparator method: correctly declared and with the class name as type instead of `self`.
+ *
+ * @param ValueObject $other Object to compare.
+ *
+ * @return bool
+ */
+ public function nonDefaultName( ValueObject $other ): bool {
+ return ( $this->value === $other->value );
+ }
+
+ /**
+ * Comparator method: incorrectly declared - more than one parameter.
+ *
+ * @param ValueObject $other Object to compare.
+ * @param mixed $param Just testing.
+ *
+ * @return bool
+ */
+ public function equalsTwoParams( $other, $param ): bool {
+ return ( $param && $this->value === $other->value );
+ }
+
+ /**
+ * Comparator method: incorrectly declared - parameter is not required.
+ *
+ * @param self|null $other Object to compare.
+ *
+ * @return bool
+ */
+ public function equalsParamNotRequired( self $other = null ): bool {
+ return ( $this->value === $other->value );
+ }
+
+ /**
+ * Comparator method: incorrectly declared - parameter is not typed.
+ *
+ * @param ValueObject $other Object to compare.
+ *
+ * @return bool
+ */
+ public function equalsParamNoType( $other ): bool {
+ return ( $this->value === $other->value );
+ }
+
+ /**
+ * Comparator method: incorrectly declared - parameter has a non-classname type.
+ *
+ * @param array $other Object to compare.
+ *
+ * @return bool
+ */
+ public function equalsParamNonClassType( array $other ): bool {
+ return ( $this->value === $other->value );
+ }
+
+ /**
+ * Comparator method: incorrectly declared - parameter has a non-existent classname type.
+ *
+ * @param ClassWhichDoesntExist $other Object to compare.
+ *
+ * @return bool
+ */
+ public function equalsParamNonExistentClassType( ClassWhichDoesntExist $other ): bool {
+ return ( $this->value === $other->value );
+ }
+}
diff --git a/tests/Polyfills/Fixtures/ValueObjectNoReturnType.php b/tests/Polyfills/Fixtures/ValueObjectNoReturnType.php
new file mode 100644
index 0000000..52775a0
--- /dev/null
+++ b/tests/Polyfills/Fixtures/ValueObjectNoReturnType.php
@@ -0,0 +1,105 @@
+value = $value;
+ }
+
+ /**
+ * Comparator method: correctly declared.
+ *
+ * @param ValueObjectNoReturnType $other Object to compare.
+ *
+ * @return bool
+ */
+ public function equals( ValueObjectNoReturnType $other ) {
+ return ( $this->value === $other->value );
+ }
+
+ /**
+ * Comparator method: correctly declared and with self as type instead of the class name.
+ *
+ * @param self $other Object to compare.
+ *
+ * @return bool
+ */
+ public function nonDefaultName( self $other ) {
+ return ( $this->value === $other->value );
+ }
+
+ /**
+ * Comparator method: incorrectly declared - more than one parameter.
+ *
+ * @param ValueObjectNoReturnType $other Object to compare.
+ * @param mixed $param Just testing.
+ *
+ * @return bool
+ */
+ public function equalsTwoParams( $other, $param ) {
+ return ( $param && $this->value === $other->value );
+ }
+
+ /**
+ * Comparator method: incorrectly declared - parameter is not required.
+ *
+ * @param self|null $other Object to compare.
+ *
+ * @return bool
+ */
+ public function equalsParamNotRequired( self $other = null ) {
+ return ( $this->value === $other->value );
+ }
+
+ /**
+ * Comparator method: incorrectly declared - parameter is not typed.
+ *
+ * @param ValueObjectNoReturnType $other Object to compare.
+ *
+ * @return bool
+ */
+ public function equalsParamNoType( $other ) {
+ return ( $this->value === $other->value );
+ }
+
+ /**
+ * Comparator method: incorrectly declared - parameter has a non-classname type.
+ *
+ * @param array $other Object to compare.
+ *
+ * @return bool
+ */
+ public function equalsParamNonClassType( array $other ) {
+ return ( $this->value === $other->value );
+ }
+
+ /**
+ * Comparator method: incorrectly declared - parameter has a non-existent classname type.
+ *
+ * @param ClassWhichDoesntExist $other Object to compare.
+ *
+ * @return bool
+ */
+ public function equalsParamNonExistentClassType( ClassWhichDoesntExist $other ) {
+ return ( $this->value === $other->value );
+ }
+}
diff --git a/tests/Polyfills/Fixtures/ValueObjectUnion.php b/tests/Polyfills/Fixtures/ValueObjectUnion.php
new file mode 100644
index 0000000..0291fc7
--- /dev/null
+++ b/tests/Polyfills/Fixtures/ValueObjectUnion.php
@@ -0,0 +1,36 @@
+value = $value;
+ }
+
+ /**
+ * Comparator method: incorrectly declared - parameter has a union type.
+ *
+ * @param self|OtherClass|array $other Object to compare.
+ *
+ * @return bool
+ */
+ public function equalsParamUnionType( self|OtherClass|array $other ): bool {
+ return ( $this->value === $other->value );
+ }
+}
diff --git a/tests/Polyfills/Fixtures/ValueObjectUnionNoReturnType.php b/tests/Polyfills/Fixtures/ValueObjectUnionNoReturnType.php
new file mode 100644
index 0000000..efa2387
--- /dev/null
+++ b/tests/Polyfills/Fixtures/ValueObjectUnionNoReturnType.php
@@ -0,0 +1,36 @@
+value = $value;
+ }
+
+ /**
+ * Comparator method: incorrectly declared - parameter has a union type.
+ *
+ * @param self|OtherClass|array $other Object to compare.
+ *
+ * @return bool
+ */
+ public function equalsParamUnionType( self|OtherClass|array $other ) {
+ return ( $this->value === $other->value );
+ }
+}
diff --git a/tests/TestCases/TestCaseTestTrait.php b/tests/TestCases/TestCaseTestTrait.php
index 54601f5..a4a80f0 100644
--- a/tests/TestCases/TestCaseTestTrait.php
+++ b/tests/TestCases/TestCaseTestTrait.php
@@ -4,6 +4,7 @@
use Exception;
use Yoast\PHPUnitPolyfills\Tests\Polyfills\AssertFileEqualsSpecializationsTest;
+use Yoast\PHPUnitPolyfills\Tests\Polyfills\Fixtures\ValueObject;
/**
* Tests for the TestCase setups.
@@ -163,4 +164,17 @@ public function testAvailabilityAssertClosedResource() {
public function testAvailabilityEqualToSpecializations() {
self::assertThat( [ 2, 3, 1 ], $this->equalToCanonicalizing( [ 3, 2, 1 ] ) );
}
+
+ /**
+ * Verify availability of trait polyfilled PHPUnit methods [14].
+ *
+ * @requires PHP 7.0
+ *
+ * @return void
+ */
+ public function testAvailabilityAssertObjectEquals() {
+ $expected = new ValueObject( 'test' );
+ $actual = new ValueObject( 'test' );
+ $this->assertObjectEquals( $expected, $actual );
+ }
}