Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

assertObjectHasProperty() and assertObjectNotHasProperty() #5229

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 31 additions & 1 deletion src/Framework/Assert.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
use PHPUnit\Framework\Constraint\LogicalOr;
use PHPUnit\Framework\Constraint\LogicalXor;
use PHPUnit\Framework\Constraint\ObjectEquals;
use PHPUnit\Framework\Constraint\ObjectHasProperty;
use PHPUnit\Framework\Constraint\RegularExpression;
use PHPUnit\Framework\Constraint\SameSize;
use PHPUnit\Framework\Constraint\StringContains;
Expand All @@ -61,7 +62,6 @@
use PHPUnit\Framework\Constraint\TraversableContainsEqual;
use PHPUnit\Framework\Constraint\TraversableContainsIdentical;
use PHPUnit\Framework\Constraint\TraversableContainsOnly;
use PHPUnit\Util\Type;
use PHPUnit\Util\Xml\Loader as XmlLoader;
use PHPUnit\Util\Xml\XmlException;

Expand Down Expand Up @@ -934,6 +934,36 @@ final public static function assertNan(mixed $actual, string $message = ''): voi
static::assertThat($actual, static::isNan(), $message);
}

/**
* Asserts that an object has a specified property.
*
* @throws ExpectationFailedException
*/
final public static function assertObjectHasProperty(string $propertyName, object $object, string $message = ''): void
{
static::assertThat(
$object,
new ObjectHasProperty($propertyName),
$message
);
}

/**
* Asserts that an object does not have a specified property.
*
* @throws ExpectationFailedException
*/
final public static function assertObjectNotHasProperty(string $propertyName, object $object, string $message = ''): void
{
static::assertThat(
$object,
new LogicalNot(
new ObjectHasProperty($propertyName)
),
$message
);
}

/**
* Asserts that two variables have the same type and value.
* Used on objects, it asserts that two variables reference
Expand Down
32 changes: 32 additions & 0 deletions src/Framework/Assert/Functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -1126,6 +1126,38 @@ function assertNan(mixed $actual, string $message = ''): void
}
}

if (!function_exists('PHPUnit\Framework\assertObjectHasProperty')) {
/**
* Asserts that an object has a specified property.
*
* @throws ExpectationFailedException
*
* @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit
*
* @see Assert::assertObjectHasProperty()
*/
function assertObjectHasProperty(string $attributeName, object $objet, string $message = ''): void
{
Assert::assertObjectHasProperty(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertObjectNotHasProperty')) {
/**
* Asserts that an object does not have a specified property.
*
* @throws ExpectationFailedException
*
* @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit
*
* @see Assert::assertObjectNotHasProperty()
*/
function assertObjectNotHasProperty(string $attributeName, object $objet, string $message = ''): void
{
Assert::assertObjectNotHasProperty(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertSame')) {
/**
* Asserts that two variables have the same type and value.
Expand Down
76 changes: 76 additions & 0 deletions src/Framework/Constraint/Object/ObjectHasProperty.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\Constraint;

use function is_object;
use function sprintf;
use ReflectionException;
use ReflectionObject;

/**
* @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit
*/
final class ObjectHasProperty extends Constraint
{
private readonly string $propertyName;

public function __construct(string $propertyName)
{
$this->propertyName = $propertyName;
}

/**
* Returns a string representation of the constraint.
*/
public function toString(): string
{
return sprintf(
'has property "%s"',
$this->propertyName
);
}

/**
* Evaluates the constraint for parameter $other. Returns true if the
* constraint is met, false otherwise.
*
* @param mixed $other value or object to evaluate
*/
protected function matches(mixed $other): bool
{
if (!is_object($other)) {
return false;
}

try {
return (new ReflectionObject($other))->hasProperty($this->propertyName);
} catch (ReflectionException) {
return false;
}
}

/**
* Returns the description of the failure.
*
* The beginning of failure messages is "Failed asserting that" in most
* cases. This method should return the second part of that sentence.
*
* @param mixed $other evaluated value or object
*/
protected function failureDescription(mixed $other): string
{
return sprintf(
'%sclass "%s" %s',
is_object($other) ? 'object of ' : '',
is_object($other) ? $other::class : $other,
$this->toString()
);
}
}
70 changes: 70 additions & 0 deletions tests/unit/Framework/Constraint/ObjectHasPropertyTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\Constraint;

use PHPUnit\Framework\ExpectationFailedException;
use PHPUnit\TestFixture\ClassWithNonPublicAttributes;
use stdClass;

/**
* @small
*/
final class ObjectHasPropertyTest extends ConstraintTestCase
{
public function testConstraintObjectHasAttribute(): void
{
$constraint = new ObjectHasProperty('privateAttribute');

$this->assertTrue($constraint->evaluate(new ClassWithNonPublicAttributes, '', true));
$this->assertFalse($constraint->evaluate(new stdClass, '', true));
$this->assertEquals('has property "privateAttribute"', $constraint->toString());
$this->assertCount(1, $constraint);

$this->expectException(ExpectationFailedException::class);
$this->expectExceptionMessage(
<<<'EOF'
Failed asserting that object of class "stdClass" has property "privateAttribute".
EOF
);
$constraint->evaluate(new stdClass);
}

public function testAssertObjectHasAttribute(): void
{
$this->assertObjectNotHasProperty('privateAttribute', new stdClass);
$this->assertObjectHasProperty('foo', new ClassWithNonPublicAttributes);
}

public function testConstraintObjectHasAttribute2(): void
{
$constraint = new ObjectHasProperty('privateAttribute');

$this->expectException(ExpectationFailedException::class);
$this->expectExceptionMessage(
<<<EOF
custom message\nFailed asserting that object of class "stdClass" has property "privateAttribute".
EOF
);
$constraint->evaluate(new stdClass, 'custom message');
}

public function testConstraintObjectHasAttributeWithNoObject(): void
{
$constraint = new ObjectHasProperty('privateAttribute');

$this->expectException(ExpectationFailedException::class);
$this->expectExceptionMessage(
<<<EOF
custom message\nFailed asserting that class "" has property "privateAttribute".
EOF
);
$constraint->evaluate('', 'custom message');
}
}