Suggested by @spriebsch:
It is a bad practice to use assertEquals() (and its inverse, assertNotEquals()) on objects without registering a custom comparator that customizes how objects are compared. Unfortunately, though, implementing custom comparators for each and every object you want to assert in your tests is inconvenient at best and overkill at worst.
The most common use case for custom comparators are Value Objects. These objects usually have an equals(self $other): bool method (or a method just like that but with a different name) for comparing two instances of the Value Object's type.
We propose a new assertion method, assertObjectEquals(), that makes custom comparison of objects convenient for this common use case:
public function assertObjectEquals(object $expected, object $actual, string $method = 'equals', string $message = '')
{
}
- A method with name
$method must exist on the $actual object
- The method must accept exactly one argument
- The respective parameter must have a declared type
- The
$expected object must be compatible with this declared type
- The method must have a declared
bool return type
If any of the aforementioned assumptions is not fulfilled or if $actual->$method($expected) returns false then the assertion fails.
An inverse of assertObjectEquals() does not make sense to us and will not be implemented.
Example
Email.php
<?php declare(strict_types=1);
final class Email
{
private string $email;
public function __construct(string $email)
{
$this->ensureIsValidEmail($email);
$this->email = $email;
}
public function asString(): string
{
return $this->email;
}
public function equals(self $other): bool
{
return $this->asString() === $other->asString();
}
private function ensureIsValidEmail(string $email): void
{
// ...
}
}
SomethingThatUsesEmailTest.php
<?php declare(strict_types=1);
use PHPUnit\Framework\TestCase;
final class SomethingThatUsesEmailTest extends TestCase
{
public function testSomething(): void
{
$a = new Email('user@example.org');
$b = new Email('user@example.org');
$c = new Email('user@example.com');
// This should pass
$this->assertObjectEquals($a, $b);
// This should fail
$this->assertObjectEquals($a, $c);
}
}
Suggested by @spriebsch:
It is a bad practice to use
assertEquals()(and its inverse,assertNotEquals()) on objects without registering a custom comparator that customizes how objects are compared. Unfortunately, though, implementing custom comparators for each and every object you want to assert in your tests is inconvenient at best and overkill at worst.The most common use case for custom comparators are Value Objects. These objects usually have an
equals(self $other): boolmethod (or a method just like that but with a different name) for comparing two instances of the Value Object's type.We propose a new assertion method,
assertObjectEquals(), that makes custom comparison of objects convenient for this common use case:$methodmust exist on the$actualobject$expectedobject must be compatible with this declared typeboolreturn typeIf any of the aforementioned assumptions is not fulfilled or if
$actual->$method($expected)returnsfalsethen the assertion fails.An inverse of
assertObjectEquals()does not make sense to us and will not be implemented.Example
Email.phpSomethingThatUsesEmailTest.php