Skip to content

Commit adc23ac

Browse files
Closes #5478
1 parent 53e630f commit adc23ac

File tree

7 files changed

+272
-2
lines changed

7 files changed

+272
-2
lines changed

.psalm/baseline.xml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,18 @@
178178
static::objectEquals($expected, $method),
179179
$message,
180180
)</code>
181+
<code>static::assertThat(
182+
$object,
183+
new LogicalNot(
184+
new ObjectHasProperty($propertyName),
185+
),
186+
$message,
187+
)</code>
188+
<code>static::assertThat(
189+
$object,
190+
new ObjectHasProperty($propertyName),
191+
$message,
192+
)</code>
181193
<code>static::assertThat($haystack, $constraint, $message)</code>
182194
<code>static::assertThat($haystack, $constraint, $message)</code>
183195
<code>static::assertThat($haystack, $constraint, $message)</code>
@@ -437,6 +449,11 @@
437449
<code>hasProperty</code>
438450
</MissingThrowsDocblock>
439451
</file>
452+
<file src="src/Framework/Constraint/Object/ObjectHasProperty.php">
453+
<MissingThrowsDocblock>
454+
<code>hasProperty</code>
455+
</MissingThrowsDocblock>
456+
</file>
440457
<file src="src/Framework/Constraint/Operator/BinaryOperator.php">
441458
<UnsafeInstantiation>
442459
<code>new static</code>

ChangeLog-9.6.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22

33
All notable changes of the PHPUnit 9.6 release series are documented in this file using the [Keep a CHANGELOG](https://keepachangelog.com/) principles.
44

5+
## [9.6.11] - 2023-MM-DD
6+
7+
### Added
8+
9+
* [#5478](https://github.com/sebastianbergmann/phpunit/pull/5478): `assertObjectHasProperty()` and `assertObjectNotHasProperty()`
10+
511
## [9.6.10] - 2023-07-10
612

713
### Changed
@@ -77,6 +83,7 @@ All notable changes of the PHPUnit 9.6 release series are documented in this fil
7783
* [#5064](https://github.com/sebastianbergmann/phpunit/issues/5064): Deprecate `PHPUnit\Framework\TestCase::getMockClass()`
7884
* [#5132](https://github.com/sebastianbergmann/phpunit/issues/5132): Deprecate `Test` suffix for abstract test case classes
7985

86+
[9.6.11]: https://github.com/sebastianbergmann/phpunit/compare/9.6.10...9.6
8087
[9.6.10]: https://github.com/sebastianbergmann/phpunit/compare/9.6.9...9.6.10
8188
[9.6.9]: https://github.com/sebastianbergmann/phpunit/compare/9.6.8...9.6.9
8289
[9.6.8]: https://github.com/sebastianbergmann/phpunit/compare/9.6.7...9.6.8

src/Framework/Assert.php

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@
7373
use PHPUnit\Framework\Constraint\LogicalXor;
7474
use PHPUnit\Framework\Constraint\ObjectEquals;
7575
use PHPUnit\Framework\Constraint\ObjectHasAttribute;
76+
use PHPUnit\Framework\Constraint\ObjectHasProperty;
7677
use PHPUnit\Framework\Constraint\RegularExpression;
7778
use PHPUnit\Framework\Constraint\SameSize;
7879
use PHPUnit\Framework\Constraint\StringContains;
@@ -1304,7 +1305,7 @@ public static function assertClassNotHasStaticAttribute(string $attributeName, s
13041305
*/
13051306
public static function assertObjectHasAttribute(string $attributeName, $object, string $message = ''): void
13061307
{
1307-
self::createWarning('assertObjectHasAttribute() is deprecated and will be removed in PHPUnit 10. Refactor your test to use assertObjectHasProperty() (PHPUnit 10.1.0+) instead.');
1308+
self::createWarning('assertObjectHasAttribute() is deprecated and will be removed in PHPUnit 10. Refactor your test to use assertObjectHasProperty() instead.');
13081309

13091310
if (!self::isValidObjectAttributeName($attributeName)) {
13101311
throw InvalidArgumentException::create(1, 'valid attribute name');
@@ -1334,7 +1335,7 @@ public static function assertObjectHasAttribute(string $attributeName, $object,
13341335
*/
13351336
public static function assertObjectNotHasAttribute(string $attributeName, $object, string $message = ''): void
13361337
{
1337-
self::createWarning('assertObjectNotHasAttribute() is deprecated and will be removed in PHPUnit 10. Refactor your test to use assertObjectNotHasProperty() (PHPUnit 10.1.0+) instead.');
1338+
self::createWarning('assertObjectNotHasAttribute() is deprecated and will be removed in PHPUnit 10. Refactor your test to use assertObjectNotHasProperty() instead.');
13381339

13391340
if (!self::isValidObjectAttributeName($attributeName)) {
13401341
throw InvalidArgumentException::create(1, 'valid attribute name');
@@ -1353,6 +1354,36 @@ public static function assertObjectNotHasAttribute(string $attributeName, $objec
13531354
);
13541355
}
13551356

1357+
/**
1358+
* Asserts that an object has a specified property.
1359+
*
1360+
* @throws ExpectationFailedException
1361+
*/
1362+
final public static function assertObjectHasProperty(string $propertyName, object $object, string $message = ''): void
1363+
{
1364+
static::assertThat(
1365+
$object,
1366+
new ObjectHasProperty($propertyName),
1367+
$message,
1368+
);
1369+
}
1370+
1371+
/**
1372+
* Asserts that an object does not have a specified property.
1373+
*
1374+
* @throws ExpectationFailedException
1375+
*/
1376+
final public static function assertObjectNotHasProperty(string $propertyName, object $object, string $message = ''): void
1377+
{
1378+
static::assertThat(
1379+
$object,
1380+
new LogicalNot(
1381+
new ObjectHasProperty($propertyName),
1382+
),
1383+
$message,
1384+
);
1385+
}
1386+
13561387
/**
13571388
* Asserts that two variables have the same type and value.
13581389
* Used on objects, it asserts that two variables reference

src/Framework/Assert/Functions.php

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1444,6 +1444,42 @@ function assertObjectNotHasAttribute(string $attributeName, $object, string $mes
14441444
}
14451445
}
14461446

1447+
if (!function_exists('PHPUnit\Framework\assertObjectHasProperty')) {
1448+
/**
1449+
* Asserts that an object has a specified property.
1450+
*
1451+
* @throws ExpectationFailedException
1452+
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
1453+
* @throws Exception
1454+
*
1455+
* @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit
1456+
*
1457+
* @see Assert::assertObjectHasProperty
1458+
*/
1459+
function assertObjectHasProperty(string $attributeName, object $object, string $message = ''): void
1460+
{
1461+
Assert::assertObjectHasProperty(...func_get_args());
1462+
}
1463+
}
1464+
1465+
if (!function_exists('PHPUnit\Framework\assertObjectNotHasProperty')) {
1466+
/**
1467+
* Asserts that an object does not have a specified property.
1468+
*
1469+
* @throws ExpectationFailedException
1470+
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
1471+
* @throws Exception
1472+
*
1473+
* @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit
1474+
*
1475+
* @see Assert::assertObjectNotHasProperty
1476+
*/
1477+
function assertObjectNotHasProperty(string $attributeName, object $object, string $message = ''): void
1478+
{
1479+
Assert::assertObjectNotHasProperty(...func_get_args());
1480+
}
1481+
}
1482+
14471483
if (!function_exists('PHPUnit\Framework\assertSame')) {
14481484
/**
14491485
* Asserts that two variables have the same type and value.
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
<?php declare(strict_types=1);
2+
/*
3+
* This file is part of PHPUnit.
4+
*
5+
* (c) Sebastian Bergmann <[email protected]>
6+
*
7+
* For the full copyright and license information, please view the LICENSE
8+
* file that was distributed with this source code.
9+
*/
10+
namespace PHPUnit\Framework\Constraint;
11+
12+
use function get_class;
13+
use function gettype;
14+
use function is_object;
15+
use function sprintf;
16+
use ReflectionObject;
17+
18+
/**
19+
* @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit
20+
*/
21+
final class ObjectHasProperty extends Constraint
22+
{
23+
/**
24+
* @var string
25+
*/
26+
private $propertyName;
27+
28+
public function __construct(string $propertyName)
29+
{
30+
$this->propertyName = $propertyName;
31+
}
32+
33+
/**
34+
* Returns a string representation of the constraint.
35+
*/
36+
public function toString(): string
37+
{
38+
return sprintf(
39+
'has property "%s"',
40+
$this->propertyName,
41+
);
42+
}
43+
44+
/**
45+
* Evaluates the constraint for parameter $other. Returns true if the
46+
* constraint is met, false otherwise.
47+
*
48+
* @param mixed $other value or object to evaluate
49+
*/
50+
protected function matches($other): bool
51+
{
52+
if (!is_object($other)) {
53+
return false;
54+
}
55+
56+
return (new ReflectionObject($other))->hasProperty($this->propertyName);
57+
}
58+
59+
/**
60+
* Returns the description of the failure.
61+
*
62+
* The beginning of failure messages is "Failed asserting that" in most
63+
* cases. This method should return the second part of that sentence.
64+
*
65+
* @param mixed $other evaluated value or object
66+
*/
67+
protected function failureDescription($other): string
68+
{
69+
if (is_object($other)) {
70+
return sprintf(
71+
'object of class "%s" %s',
72+
get_class($other),
73+
$this->toString(),
74+
);
75+
}
76+
77+
return sprintf(
78+
'"%s" (%s) %s',
79+
$other,
80+
gettype($other),
81+
$this->toString(),
82+
);
83+
}
84+
}

tests/unit/Framework/AssertTest.php

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2122,6 +2122,38 @@ public function testTwoObjectsCanBeAssertedToBeEqualUsingComparisonMethod(): voi
21222122
$this->fail();
21232123
}
21242124

2125+
public function testObjectHasPropertyCanBeAsserted(): void
2126+
{
2127+
$objectWithProperty = new stdClass;
2128+
$objectWithProperty->theProperty = 'value';
2129+
2130+
$this->assertObjectHasProperty('theProperty', $objectWithProperty);
2131+
2132+
try {
2133+
$this->assertObjectHasProperty('doesNotExist', $objectWithProperty);
2134+
} catch (AssertionFailedError $e) {
2135+
return;
2136+
}
2137+
2138+
$this->fail();
2139+
}
2140+
2141+
public function testObjectDoesNotHavePropertyCanBeAsserted(): void
2142+
{
2143+
$objectWithProperty = new stdClass;
2144+
$objectWithProperty->theProperty = 'value';
2145+
2146+
$this->assertObjectNotHasProperty('doesNotExist', $objectWithProperty);
2147+
2148+
try {
2149+
$this->assertObjectNotHasProperty('theProperty', $objectWithProperty);
2150+
} catch (AssertionFailedError $e) {
2151+
return;
2152+
}
2153+
2154+
$this->fail();
2155+
}
2156+
21252157
protected function sameValues(): array
21262158
{
21272159
$object = new SampleClass(4, 8, 15);
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
<?php declare(strict_types=1);
2+
/*
3+
* This file is part of PHPUnit.
4+
*
5+
* (c) Sebastian Bergmann <[email protected]>
6+
*
7+
* For the full copyright and license information, please view the LICENSE
8+
* file that was distributed with this source code.
9+
*/
10+
namespace PHPUnit\Framework\Constraint;
11+
12+
use PHPUnit\Framework\ExpectationFailedException;
13+
use PHPUnit\Framework\TestCase;
14+
use stdClass;
15+
16+
/**
17+
* @covers \PHPUnit\Framework\Constraint\ObjectHasProperty
18+
*
19+
* @small
20+
*/
21+
final class ObjectHasPropertyTest extends TestCase
22+
{
23+
public function testCanBeEvaluated(): void
24+
{
25+
$constraint = new ObjectHasProperty('theProperty');
26+
27+
$objectWithProperty = new stdClass;
28+
$objectWithProperty->theProperty = 'value';
29+
30+
$this->assertTrue($constraint->evaluate($objectWithProperty, '', true));
31+
$this->assertFalse($constraint->evaluate(new stdClass, '', true));
32+
$this->assertFalse($constraint->evaluate(null, '', true));
33+
34+
$this->expectException(ExpectationFailedException::class);
35+
$this->expectExceptionMessage('Failed asserting that object of class "stdClass" has property "theProperty".');
36+
37+
$constraint->evaluate(new stdClass);
38+
}
39+
40+
public function testHandlesNonObjectsGracefully(): void
41+
{
42+
$constraint = new ObjectHasProperty('theProperty');
43+
44+
$this->expectException(ExpectationFailedException::class);
45+
$this->expectExceptionMessage('Failed asserting that "non-object" (string) has property "theProperty".');
46+
47+
$constraint->evaluate('non-object');
48+
}
49+
50+
public function testCanBeRepresentedAsString(): void
51+
{
52+
$constraint = new ObjectHasProperty('theProperty');
53+
54+
$this->assertSame('has property "theProperty"', $constraint->toString());
55+
}
56+
57+
public function testIsCountable(): void
58+
{
59+
$constraint = new ObjectHasProperty('theProperty');
60+
61+
$this->assertCount(1, $constraint);
62+
}
63+
}

0 commit comments

Comments
 (0)