Skip to content

Commit 57b960d

Browse files
committed
AssociativeArray: Allow passing static values as constraint
1 parent d9855ea commit 57b960d

File tree

6 files changed

+100
-101
lines changed

6 files changed

+100
-101
lines changed

README.md

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ All options do the same, the only difference is that the static class and trait
4848

4949
The [`AssociativeArray` constraint](https://github.com/PhrozenByte/phpunit-array-asserts/blob/master/src/Constraint/AssociativeArray.php) asserts that a value is an associative array matching a given structure and that the array's items pass other constraints.
5050

51-
Any native array and `ArrayAccess` object is considered an associative array, no matter which keys they use. However, the array's items are applied to the matching constraint (parameter `$consotraints`). By default, missing items will fail the constraint (parameter `$allowMissing`, defaults to `false`). Additional items will be ignored by default (parameter `$allowAdditional`, defaults to `true`). If you want the constraint to fail when additional items exist, set this option to `true`, however, please note that this works for native arrays only. The expected keys and constraints to apply, as well as whether missing and/or additional items should fail the constraint, are passed in the constructor.
51+
Any native array and `ArrayAccess` object is considered an associative array, no matter which keys they use. However, the array's items are applied to the matching constraint (parameter `$consotraints`). By default, missing items will fail the constraint (parameter `$allowMissing`, defaults to `false`). Additional items will be ignored by default (parameter `$allowAdditional`, defaults to `true`). If you want the constraint to fail when additional items exist, set this option to `true`, however, please note that this works for native arrays only. The expected keys and constraints to apply, as well as whether missing and/or additional items should fail the constraint, are passed in the constructor. Constraints can either be arbitrary `Constraint` instances (e.g. `PHPUnit\Framework\Constraint\StringContains`), or any static value, requiring exact matches of the values.
5252

5353
The `ArrayAssertsTrait` trait exposes two public methods for the `AssociativeArray` constraint: Use `ArrayAssertsTrait::assertAssociativeArray()` to perform an assertion, and `ArrayAssertsTrait::associativeArray()` to create a new instance of the `AssociativeArray` constraint.
5454

@@ -57,10 +57,10 @@ The `ArrayAssertsTrait` trait exposes two public methods for the `AssociativeArr
5757
```php
5858
// using `\PhrozenByte\PHPUnitArrayAsserts\ArrayAssertsTrait` trait
5959
ArrayAssertsTrait::assertAssociativeArray(
60-
array $constraints, // an associative array with the expected keys and constraints to apply
60+
array $constraints, // an array with the expected keys and constraints to apply
6161
array|ArrayAccess $array, // the associative array to check
62-
bool $allowMissing = false, // whether missing items should fail the constraint
63-
bool $allowAdditional = true, // whether additional items should fail the constraint
62+
bool $allowMissing = false, // whether missing items fail the constraint
63+
bool $allowAdditional = true, // whether additional items fail the constraint
6464
string $message = '' // additional information about the test
6565
);
6666

@@ -87,7 +87,7 @@ $data = [
8787
// - "options" with another associative array with the key "panic", whose value must be a boolean
8888
$this->assertAssociativeArray([
8989
'id' => $this->isType(IsType::TYPE_INT),
90-
'name' => $this->identicalTo('Arthur Dent'),
90+
'name' => 'Arthur Dent',
9191
'options' => $this->associativeArray([ 'panic' => $this->isType(IsType::TYPE_BOOL) ], true)
9292
], $data);
9393
```
@@ -100,17 +100,17 @@ $data = [
100100
];
101101

102102
$this->assertAssociativeArray([
103-
'answer' => $this->identicalTo(42)
103+
'answer' => 42
104104
], $data);
105105

106106
// Will fail with the following message:
107107
//
108108
// Failed asserting that associative array matches constraints.
109-
// +----------+-------+--------------------------+
110-
// | Key | Value | Constraint |
111-
// +----------+-------+--------------------------+
112-
// | 'answer' | 21 | Value is identical to 42 |
113-
// +----------+-------+--------------------------+
109+
// +----------+-------+----------------------+
110+
// | Key | Value | Constraint |
111+
// +----------+-------+----------------------+
112+
// | 'answer' | 21 | Value is equal to 42 |
113+
// +----------+-------+----------------------+
114114
// [ ] Allow missing; [x] Allow additional
115115
```
116116

@@ -321,7 +321,7 @@ class MyTest extends TestCase
321321

322322
$this->assertAssociativeArray([
323323
'id' => $this->isType(IsType::TYPE_INT),
324-
'name' => $this->identicalTo('Arthur Dent'),
324+
'name' => 'Arthur Dent',
325325
'options' => $this->associativeArray([ 'panic' => $this->isType(IsType::TYPE_BOOL) ])
326326
], $responseData['users'][0]);
327327
}

src/ArrayAssertsTrait.php

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,12 @@ trait ArrayAssertsTrait
5252
* Asserts that a value is an associative array matching a given structure
5353
* and that the array's items pass other constraints.
5454
*
55-
* @param Constraint[] $constraints an associative array with the expected keys and constraints to apply
56-
* @param array|ArrayAccess $array the associative array to check
57-
* @param bool $allowMissing whether missing items should fail the constraint (defaults to FALSE)
58-
* @param bool $allowAdditional whether additional items should fail the constraint (defaults to TRUE);
59-
* this option works for native arrays only
60-
* @param string $message additional information about the test
55+
* @param Constraint[]|mixed[] $constraints an array with the expected keys and constraints to apply
56+
* @param array|ArrayAccess $array the associative array to check
57+
* @param bool $allowMissing whether missing items fail the constraint (defaults to FALSE)
58+
* @param bool $allowAdditional whether additional items fail the constraint (defaults to TRUE);
59+
* this option works for native arrays only
60+
* @param string $message additional information about the test
6161
*
6262
* @throws ExpectationFailedException
6363
* @throws InvalidArgumentException
@@ -85,10 +85,10 @@ public static function assertAssociativeArray(
8585
/**
8686
* Returns a new instance of the AssociativeArray constraint.
8787
*
88-
* @param Constraint[] $constraints an associative array with the expected keys and constraints to apply
89-
* @param bool $allowMissing whether missing items should fail the constraint (defaults to FALSE)
90-
* @param bool $allowAdditional whether additional items should fail the constraint (defaults to TRUE);
91-
* this option works for native arrays only
88+
* @param Constraint[]|mixed[] $constraints an array with the expected keys and constraints to apply
89+
* @param bool $allowMissing whether missing items fail the constraint (defaults to FALSE)
90+
* @param bool $allowAdditional whether additional items fail the constraint (defaults to TRUE);
91+
* this option works for native arrays only
9292
*
9393
* @return AssociativeArray
9494
*

src/Constraint/AssociativeArray.php

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
use ArrayAccess;
2323
use LucidFrame\Console\ConsoleTable;
2424
use PHPUnit\Framework\Constraint\Constraint;
25+
use PHPUnit\Framework\Constraint\IsEqual;
2526
use PHPUnit\Framework\InvalidArgumentException;
2627

2728
/**
@@ -35,7 +36,9 @@
3536
*
3637
* The expected keys and constraints to apply, as well as whether additional
3738
* and/or missing items should fail the constraint, are passed in the
38-
* constructor.
39+
* constructor. Constraints can either be arbitrary `Constraint` instances
40+
* (e.g. `PHPUnit\Framework\Constraint\StringContains`), or any static value,
41+
* requiring exact matches of the values.
3942
*/
4043
class AssociativeArray extends Constraint
4144
{
@@ -51,21 +54,23 @@ class AssociativeArray extends Constraint
5154
/**
5255
* AssociativeArray constructor.
5356
*
54-
* @param Constraint[] $constraints an associative array with the expected keys and constraints to apply
55-
* @param bool $allowMissing whether missing items should fail the constraint (defaults to FALSE)
56-
* @param bool $allowAdditional whether additional items should fail the constraint (defaults to TRUE);
57-
* this option works for native arrays only
57+
* @param Constraint[]|mixed[] $constraints an array with the expected keys and constraints to apply
58+
* @param bool $allowMissing whether missing items fail the constraint (defaults to FALSE)
59+
* @param bool $allowAdditional whether additional items fail the constraint (defaults to TRUE);
60+
* this option works for native arrays only
5861
*
5962
* @throws InvalidArgumentException
6063
*/
6164
public function __construct(array $constraints, bool $allowMissing = false, bool $allowAdditional = true)
6265
{
63-
$isNoConstraint = static function ($constraint): bool { return !($constraint instanceof Constraint); };
64-
if (array_filter($constraints, $isNoConstraint)) {
65-
throw InvalidArgumentException::create(1, sprintf('array of %s', Constraint::class));
66+
foreach ($constraints as $key => $constraint) {
67+
if (!($constraint instanceof Constraint)) {
68+
$constraint = new IsEqual($constraint);
69+
}
70+
71+
$this->constraints[$key] = $constraint;
6672
}
6773

68-
$this->constraints = $constraints;
6974
$this->allowMissing = $allowMissing;
7075
$this->allowAdditional = $allowAdditional;
7176
}

tests/TestCase.php

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,17 +43,21 @@ abstract class TestCase extends \PHPUnit\Framework\TestCase
4343
/**
4444
* Mocks a constraint.
4545
*
46-
* @param Constraint $constraint the original constraint
46+
* @param Constraint|mixed $constraint the original constraint
4747
* @param InvocationOrder[] $invocationRules invocation rules for public methods
4848
* @param mixed[]|null $evaluateParameters the expected arguments passed to the `evaluate()` method
4949
*
50-
* @return Constraint
50+
* @return Constraint|mixed
5151
*/
5252
protected function mockConstraint(
53-
Constraint $constraint,
53+
$constraint,
5454
array $invocationRules = [],
5555
array $evaluateParameters = null
56-
): Constraint {
56+
) {
57+
if (!($constraint instanceof Constraint)) {
58+
return $constraint;
59+
}
60+
5761
/** @var Constraint|MockObject $mockedConstraint */
5862
$mockedConstraint = $this->getMockBuilder(get_class($constraint))
5963
->disableOriginalConstructor()

tests/Unit/Constraint/AssociativeArrayTest.php

Lines changed: 27 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -34,42 +34,13 @@
3434
*/
3535
class AssociativeArrayTest extends TestCase
3636
{
37-
/**
38-
* @dataProvider dataProviderInvalidParameters
39-
*
40-
* @param Constraint[] $constraints
41-
* @param bool $allowMissing
42-
* @param bool $allowAdditional
43-
* @param string $expectedException
44-
* @param string $expectedExceptionMessage
45-
*/
46-
public function testInvalidParameters(
47-
array $constraints,
48-
bool $allowMissing,
49-
bool $allowAdditional,
50-
string $expectedException,
51-
string $expectedExceptionMessage
52-
): void {
53-
$this->assertCallableThrows(static function () use ($constraints, $allowMissing, $allowAdditional) {
54-
new AssociativeArray($constraints, $allowMissing, $allowAdditional);
55-
}, $expectedException, $expectedExceptionMessage);
56-
}
57-
58-
/**
59-
* @return array[]
60-
*/
61-
public function dataProviderInvalidParameters(): array
62-
{
63-
return $this->getTestDataSets('testInvalidParameters');
64-
}
65-
6637
/**
6738
* @dataProvider dataProviderSelfDescribing
6839
*
69-
* @param Constraint[] $constraints
70-
* @param bool $allowMissing
71-
* @param bool $allowAdditional
72-
* @param string $expectedDescription
40+
* @param Constraint[]|mixed[] $constraints
41+
* @param bool $allowMissing
42+
* @param bool $allowAdditional
43+
* @param string $expectedDescription
7344
*/
7445
public function testSelfDescribing(
7546
array $constraints,
@@ -94,11 +65,11 @@ public function dataProviderSelfDescribing(): array
9465
/**
9566
* @dataProvider dataProviderEvaluate
9667
*
97-
* @param Constraint[] $constraints
98-
* @param bool $allowMissing
99-
* @param bool $allowAdditional
100-
* @param mixed $other
101-
* @param mixed[] $expectedEvaluationValues
68+
* @param Constraint[]|mixed[] $constraints
69+
* @param bool $allowMissing
70+
* @param bool $allowAdditional
71+
* @param mixed $other
72+
* @param mixed[] $expectedEvaluationValues
10273
*/
10374
public function testEvaluate(
10475
array $constraints,
@@ -139,12 +110,12 @@ public function dataProviderEvaluate(): array
139110
/**
140111
* @dataProvider dataProviderEvaluateFail
141112
*
142-
* @param Constraint[] $constraints
143-
* @param bool $allowMissing
144-
* @param bool $allowAdditional
145-
* @param mixed $other
146-
* @param mixed[] $expectedEvaluationValues
147-
* @param string $expectedExceptionMessage
113+
* @param Constraint[]|mixed[] $constraints
114+
* @param bool $allowMissing
115+
* @param bool $allowAdditional
116+
* @param mixed $other
117+
* @param mixed[] $expectedEvaluationValues
118+
* @param string $expectedExceptionMessage
148119
*/
149120
public function testEvaluateFail(
150121
array $constraints,
@@ -187,11 +158,11 @@ public function dataProviderEvaluateFail(): array
187158
/**
188159
* @dataProvider dataProviderPreEvaluateFail
189160
*
190-
* @param Constraint[] $constraints
191-
* @param bool $allowMissing
192-
* @param bool $allowAdditional
193-
* @param mixed $other
194-
* @param string $expectedExceptionMessage
161+
* @param Constraint[]|mixed[] $constraints
162+
* @param bool $allowMissing
163+
* @param bool $allowAdditional
164+
* @param mixed $other
165+
* @param string $expectedExceptionMessage
195166
*/
196167
public function testPreEvaluateFail(
197168
array $constraints,
@@ -222,10 +193,10 @@ public function dataProviderPreEvaluateFail(): array
222193
/**
223194
* @dataProvider dataProviderCountable
224195
*
225-
* @param Constraint[] $constraints
226-
* @param bool $allowMissing
227-
* @param bool $allowAdditional
228-
* @param int $expectedCount
196+
* @param Constraint[]|mixed[] $constraints
197+
* @param bool $allowMissing
198+
* @param bool $allowAdditional
199+
* @param int $expectedCount
229200
*/
230201
public function testCountable(
231202
array $constraints,
@@ -248,9 +219,9 @@ public function dataProviderCountable(): array
248219
}
249220

250221
/**
251-
* @param Constraint[] $constraints
252-
* @param InvocationOrder[] $invocationRules
253-
* @param mixed[][] $evaluateParameters
222+
* @param Constraint[]|mixed[] $constraints
223+
* @param InvocationOrder[] $invocationRules
224+
* @param mixed[][] $evaluateParameters
254225
*
255226
* @return Constraint[]
256227
*/

tests/data/AssociativeArrayTest.yml

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,9 @@
6060
toString: matches something
6161
matches: false
6262
count: 4
63+
constraintsStatic: &constraintsStatic
64+
constraints:
65+
item: static value
6366

6467
constraintsNoneMatch: &constraintsNoneMatch
6568
<<: *constraintsNone
@@ -76,16 +79,6 @@
7679
item2: { options: { matches: true } }
7780
item3: { options: { matches: true } }
7881

79-
testInvalidParameters:
80-
- constraints:
81-
- "this is no constraint"
82-
allowMissing: false
83-
allowAdditional: true
84-
expectedException: PHPUnit\Framework\InvalidArgumentException
85-
expectedExceptionMessage: >-
86-
Argument #1 of PhrozenByte\PHPUnitArrayAsserts\Constraint\AssociativeArray::__construct()
87-
must be an array of PHPUnit\Framework\Constraint\Constraint
88-
8982
testSelfDescribing:
9083
- <<: [ *constraintsNone, *paramsDefault ]
9184
expectedDescription: is an associative array
@@ -143,10 +136,21 @@ testSelfDescribing:
143136
the key 'item2' whose value is funny, and/or
144137
the key 'item3' whose value matches something
145138
139+
- <<: [ *constraintsStatic, *paramsDefault ]
140+
expectedDescription: >-
141+
is an associative array that
142+
has the key 'item' whose value is equal to 'static value', and
143+
any other item
144+
146145
testEvaluate:
147146
- <<: [ *constraintsNoneMatch, *paramsDefault ]
148147
other: ~
149148
expectedEvaluationValues: {}
149+
- <<: [ *constraintsStatic, *paramsDefault ]
150+
other:
151+
item: static value
152+
expectedEvaluationValues:
153+
item: static value
150154

151155
- <<: [ *constraintsNoneMatch, *paramsDefault ]
152156
other: {}
@@ -239,6 +243,19 @@ testEvaluateFail:
239243
| 'item3' | | Value matches something |
240244
+---------+--------+-------------------------+
241245
[x] Allow missing; [x] Allow additional
246+
- <<: [ *constraintsStatic, *paramsDefault ]
247+
other:
248+
item: other value
249+
expectedEvaluationValues:
250+
item: other value
251+
expectedExceptionMessage: |
252+
Failed asserting that associative array matches constraints.
253+
+--------+---------------+----------------------------------+
254+
| Key | Value | Constraint |
255+
+--------+---------------+----------------------------------+
256+
| 'item' | 'other value' | Value is equal to 'static value' |
257+
+--------+---------------+----------------------------------+
258+
[ ] Allow missing; [x] Allow additional
242259
243260
testPreEvaluateFail:
244261
- <<: [ *constraintsSingle, *paramsDefault ]
@@ -340,3 +357,5 @@ testCountable:
340357
expectedCount: 1
341358
- <<: [ *constraintsMultiple, *paramsDefault ]
342359
expectedCount: 10
360+
- <<: [ *constraintsStatic, *paramsDefault ]
361+
expectedCount: 2

0 commit comments

Comments
 (0)