Skip to content

Commit

Permalink
AssertObjectNotEquals trait: polyfill the Assert::assertObjectNotEqua…
Browse files Browse the repository at this point in the history
…ls() method

PHPUnit 11.2.0 introduced the new `Assert::assertObjectNotEquals()` method.

This commit:
* Adds two traits with the same name.
    One to polyfill the method when not available in PHPUnit.
    The other to allow for `use`-ing the trait in PHPUnit versions in which the method is already natively available.
* Logic to the custom autoloader which will load the correct trait depending on the PHPUnit version used.
* Adds tests.

As the polyfill contains logic to match the PHPUnit native implementation as closely as possible, while still being PHP and PHPUnit cross-version compatible, extensive unit tests have been added to ensure the behaviour of the polyfill matches that of the original function.

Includes:
* Adding information on the new polyfill to the README.
* Adding the new polyfill to the existing `TestCases` classes.
* Updating the class docs for the `InvalidComparisonMethodException` and the `ComparatorValidator` classes.

Refs:
* sebastianbergmann/phpunit#5811
* sebastianbergmann/phpunit@8e3b7c1

Co-authored-by: Sebastian Bergmann <[email protected]>
  • Loading branch information
jrfnl and sebastianbergmann committed Jul 17, 2024
1 parent ab9c8bf commit e6a65ff
Show file tree
Hide file tree
Showing 11 changed files with 677 additions and 3 deletions.
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,17 @@ Refactoring tests which still use `Assert::assertArraySubset()` to use the new a
[`Assert::assertArrayIsIdenticalToArrayOnlyConsideringListOfKeys()`]: https://docs.phpunit.de/en/main/assertions.html#assertarrayisidenticaltoarrayonlyconsideringlistofkeys
[`Assert::assertArrayIsIdenticalToArrayIgnoringListOfKeys()`]: https://docs.phpunit.de/en/main/assertions.html#assertarrayisidenticaltoarrayignoringlistofkeys

#### PHPUnit < 11.2.0: `Yoast\PHPUnitPolyfills\Polyfills\AssertObjectNotEquals`

Polyfills the [`Assert::assertObjectNotEquals()`] method to verify two (value) objects are **_not_** considered equal.
This is the sister-method to the PHPUnit 9.4+ `Assert::assertObjectEquals()` method.

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 `assertObjectNotEquals()` assertion was introduced in PHPUnit 11.2.0.

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


### TestCases

Expand Down
21 changes: 21 additions & 0 deletions phpunitpolyfills-autoload.php
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,10 @@ public static function load( $className ) {
self::loadAssertArrayWithListKeys();
return true;

case 'Yoast\PHPUnitPolyfills\Polyfills\AssertObjectNotEquals':
self::loadAssertObjectNotEquals();
return true;

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

/**
* Load the AssertObjectNotEquals 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 loadAssertObjectNotEquals() {
if ( \method_exists( Assert::class, 'assertObjectNotEquals' ) === false ) {
// PHPUnit < 11.2.0.
require_once __DIR__ . '/src/Polyfills/AssertObjectNotEquals.php';
return;
}

// PHPUnit >= 11.2.0.
require_once __DIR__ . '/src/Polyfills/AssertObjectNotEquals_Empty.php';
}

/**
* Load the appropriate TestCase class based on the PHPUnit version being used.
*
Expand Down
4 changes: 2 additions & 2 deletions src/Exceptions/InvalidComparisonMethodException.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
use Exception;

/**
* Exception used for all errors throw by the polyfill for the `assertObjectEquals()` assertion.
* Exception used for all errors throw by the polyfill for the `assertObjectEquals()` and the `assertObjectNotEquals()` assertions.
*
* PHPUnit natively throws a range of different exceptions.
* The polyfill throws just one exception type with different messages.
* The polyfills throw just one exception type with different messages.
*/
final class InvalidComparisonMethodException extends Exception {

Expand Down
3 changes: 2 additions & 1 deletion src/Helpers/ComparatorValidator.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
* This class is only intended for internal use by PHPUnit Polyfills and is not part of the public API.
* This also means that it has no promise of backward compatibility.
*
* End-users should use the {@see \Yoast\PHPUnitPolyfills\Polyfills\AssertObjectEquals} trait instead.
* End-users should use the {@see \Yoast\PHPUnitPolyfills\Polyfills\AssertObjectEquals} and/or the
* {@see \Yoast\PHPUnitPolyfills\Polyfills\AssertObjectNotEquals} trait instead.
* ---------------------------------------------------------------------------------------------
*
* @internal
Expand Down
111 changes: 111 additions & 0 deletions src/Polyfills/AssertObjectNotEquals.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
<?php

namespace Yoast\PHPUnitPolyfills\Polyfills;

use TypeError;
use Yoast\PHPUnitPolyfills\Exceptions\InvalidComparisonMethodException;
use Yoast\PHPUnitPolyfills\Helpers\ComparatorValidator;

/**
* Polyfill the Assert::assertObjectNotEquals() method.
*
* Introduced in PHPUnit 11.2.0.
*
* The polyfill implementation closely matches the PHPUnit native implementation with the exception
* of the thrown exceptions.
*
* @link https://github.com/sebastianbergmann/phpunit/issues/5811
* @link https://github.com/sebastianbergmann/phpunit/commit/8e3b7c18506312df0676f2e079c414cc56b49f69
*/
trait AssertObjectNotEquals {

/**
* Asserts that two objects are considered _not_ equal based on a custom object comparison
* using a comparator method in the target object.
*
* The custom comparator method is expected to have the following method
* signature: `equals(self $other): bool` (or similar with a different method name).
*
* Basically, the assertion checks the following:
* - A method with name $method must exist on the $actual object.
* - The method must accept exactly one argument and this argument must be required.
* - This parameter must have a classname-based declared type.
* - The $expected object must be compatible with this declared type.
* - The method must have a declared bool return type.
*
* @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.
*/
final public static function assertObjectNotEquals( $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 assertObjectNotEquals() must be an object, %s given',
\gettype( $expected )
)
);
}

if ( \is_object( $actual ) === false ) {
throw new TypeError(
\sprintf(
'Argument 2 passed to assertObjectNotEquals() must be an object, %s given',
\gettype( $actual )
)
);
}

if ( \is_scalar( $method ) === false ) {
throw new TypeError(
\sprintf(
'Argument 3 passed to assertObjectNotEquals() must be of the type string, %s given',
\gettype( $method )
)
);
}
else {
$method = (string) $method;
}

/*
* Validate the comparator method requirements.
*
* If the method does not validate, an InvalidComparisonMethodException is thrown,
* which will cause the test to error out.
*/
try {
ComparatorValidator::isValid( $expected, $actual, $method );
} catch ( InvalidComparisonMethodException $e ) {
// Rethrow to ensure a stack trace shows the exception comes from this method, not the helper.
throw $e;
}

/*
* Execute the comparator method.
*/
$result = $actual->{$method}( $expected );

$msg = \sprintf(
'Failed asserting that two objects are not equal. The objects are equal according to %s::%s()',
\get_class( $actual ),
$method
);

if ( $message !== '' ) {
$msg = $message . \PHP_EOL . $msg;
}

static::assertFalse( $result, $msg );
}
}
8 changes: 8 additions & 0 deletions src/Polyfills/AssertObjectNotEquals_Empty.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php

namespace Yoast\PHPUnitPolyfills\Polyfills;

/**
* Empty trait for use with PHPUnit >= 11.2.0 in which this polyfill is not needed.
*/
trait AssertObjectNotEquals {}
2 changes: 2 additions & 0 deletions src/TestCases/TestCasePHPUnitGte8.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use Yoast\PHPUnitPolyfills\Polyfills\AssertionRenames;
use Yoast\PHPUnitPolyfills\Polyfills\AssertIsList;
use Yoast\PHPUnitPolyfills\Polyfills\AssertObjectEquals;
use Yoast\PHPUnitPolyfills\Polyfills\AssertObjectNotEquals;
use Yoast\PHPUnitPolyfills\Polyfills\AssertObjectProperty;
use Yoast\PHPUnitPolyfills\Polyfills\EqualToSpecializations;
use Yoast\PHPUnitPolyfills\Polyfills\ExpectExceptionMessageMatches;
Expand All @@ -32,6 +33,7 @@ abstract class TestCase extends PHPUnit_TestCase {
use AssertionRenames;
use AssertIsList;
use AssertObjectEquals;
use AssertObjectNotEquals;
use AssertObjectProperty;
use EqualToSpecializations;
use ExpectExceptionMessageMatches;
Expand Down
2 changes: 2 additions & 0 deletions src/TestCases/TestCasePHPUnitLte7.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use Yoast\PHPUnitPolyfills\Polyfills\AssertIsList;
use Yoast\PHPUnitPolyfills\Polyfills\AssertIsType;
use Yoast\PHPUnitPolyfills\Polyfills\AssertObjectEquals;
use Yoast\PHPUnitPolyfills\Polyfills\AssertObjectNotEquals;
use Yoast\PHPUnitPolyfills\Polyfills\AssertObjectProperty;
use Yoast\PHPUnitPolyfills\Polyfills\AssertStringContains;
use Yoast\PHPUnitPolyfills\Polyfills\EqualToSpecializations;
Expand All @@ -37,6 +38,7 @@ abstract class TestCase extends PHPUnit_TestCase {
use AssertIsList;
use AssertIsType;
use AssertObjectEquals;
use AssertObjectNotEquals;
use AssertObjectProperty;
use AssertStringContains;
use EqualToSpecializations;
Expand Down
2 changes: 2 additions & 0 deletions src/TestCases/XTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
use Yoast\PHPUnitPolyfills\Polyfills\AssertIsList;
use Yoast\PHPUnitPolyfills\Polyfills\AssertIsType;
use Yoast\PHPUnitPolyfills\Polyfills\AssertObjectEquals;
use Yoast\PHPUnitPolyfills\Polyfills\AssertObjectNotEquals;
use Yoast\PHPUnitPolyfills\Polyfills\AssertObjectProperty;
use Yoast\PHPUnitPolyfills\Polyfills\AssertStringContains;
use Yoast\PHPUnitPolyfills\Polyfills\EqualToSpecializations;
Expand Down Expand Up @@ -43,6 +44,7 @@ abstract class XTestCase extends PHPUnit_TestCase {
use AssertIsList;
use AssertIsType;
use AssertObjectEquals;
use AssertObjectNotEquals;
use AssertObjectProperty;
use AssertStringContains;
use EqualToSpecializations;
Expand Down
Loading

0 comments on commit e6a65ff

Please sign in to comment.