-
-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Description
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
$methodmust exist on the$actualobject - The method must accept exactly one argument
- The respective parameter must have a declared type
- The
$expectedobject must be compatible with this declared type - The method must have a declared
boolreturn 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);
}
}