Skip to content

Commit f582683

Browse files
rejinkaNyholm
authored andcommitted
Support for named arguments (#79)
* Support for named arguments Since 3.3, Symfony supports named arguments. Therefore casting the argumentIndex to an integer does not support all cases anymore. * Better wording for named arguments. * Throws an exception, if the argument is a named but the empty string * test fixed
1 parent 86f757d commit f582683

File tree

2 files changed

+179
-13
lines changed

2 files changed

+179
-13
lines changed

PhpUnit/DefinitionHasArgumentConstraint.php

Lines changed: 48 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@
99

1010
class DefinitionHasArgumentConstraint extends Constraint
1111
{
12+
13+
/**
14+
* @var int|string
15+
*/
1216
private $argumentIndex;
1317
private $expectedValue;
1418
private $checkExpectedValue;
@@ -17,13 +21,36 @@ public function __construct($argumentIndex, $expectedValue, $checkExpectedValue
1721
{
1822
parent::__construct();
1923

20-
$this->argumentIndex = (integer)$argumentIndex;
24+
if (!(is_string($argumentIndex) || (is_int($argumentIndex) && $argumentIndex >= 0))) {
25+
throw new \InvalidArgumentException('Expected either a string or a positive integer for $argumentIndex.');
26+
}
27+
28+
if (is_string($argumentIndex)) {
29+
if ('' === $argumentIndex) {
30+
throw new \InvalidArgumentException('A named argument must begin with a "$".');
31+
}
32+
33+
if ('$' !== $argumentIndex[0]) {
34+
throw new \InvalidArgumentException(
35+
sprintf('Unknown argument "%s". Did you mean "$%s"?', $argumentIndex, $argumentIndex)
36+
);
37+
}
38+
}
39+
40+
$this->argumentIndex = $argumentIndex;
2141
$this->expectedValue = $expectedValue;
2242
$this->checkExpectedValue = $checkExpectedValue;
2343
}
2444

2545
public function toString()
2646
{
47+
if (is_string($this->argumentIndex)) {
48+
return sprintf(
49+
'has an argument named "%s" with the given value',
50+
$this->argumentIndex
51+
);
52+
}
53+
2754
return sprintf(
2855
'has an argument with index %d with the given value',
2956
$this->argumentIndex
@@ -65,13 +92,13 @@ private function evaluateArgumentIndex(Definition $definition, $returnResult)
6592
return false;
6693
}
6794

68-
$this->fail(
69-
$definition,
70-
sprintf(
71-
'The definition has no argument with index %d',
72-
$this->argumentIndex
73-
)
74-
);
95+
if (is_string($this->argumentIndex)) {
96+
$message = sprintf('The definition has no argument named "%s"', $this->argumentIndex);
97+
} else {
98+
$message = sprintf('The definition has no argument with index %d', $this->argumentIndex);
99+
}
100+
101+
$this->fail($definition, $message);
75102
}
76103

77104
return true;
@@ -88,15 +115,23 @@ private function evaluateArgumentValue(Definition $definition, $returnResult)
88115
return false;
89116
}
90117

91-
$this->fail(
92-
$definition,
93-
sprintf(
118+
if (is_string($this->argumentIndex)) {
119+
$message = sprintf(
120+
'The value of argument named "%s" (%s) is not equal to the expected value (%s)',
121+
$this->argumentIndex,
122+
$this->exporter->export($actualValue),
123+
$this->exporter->export($this->expectedValue)
124+
);
125+
} else {
126+
$message = sprintf(
94127
'The value of argument with index %d (%s) is not equal to the expected value (%s)',
95128
$this->argumentIndex,
96129
$this->exporter->export($actualValue),
97130
$this->exporter->export($this->expectedValue)
98-
)
99-
);
131+
);
132+
}
133+
134+
$this->fail($definition, $message);
100135
}
101136

102137
return true;

Tests/PhpUnit/DefinitionHasArgumentConstraintTest.php

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
namespace Matthias\SymfonyDependencyInjectionTest\Tests\PhpUnit\DependencyInjection;
44

55
use Matthias\SymfonyDependencyInjectionTest\PhpUnit\DefinitionHasArgumentConstraint;
6+
use PHPUnit\Framework\ExpectationFailedException;
67
use PHPUnit\Framework\TestCase;
8+
use stdClass;
79
use Symfony\Component\DependencyInjection\Definition;
810
use Symfony\Component\DependencyInjection\DefinitionDecorator;
911

@@ -45,4 +47,133 @@ public function definitionProvider()
4547
array($decoratedDefinitionWithArguments, 1, $rightValue, true),
4648
);
4749
}
50+
51+
/**
52+
* @test
53+
* @dataProvider invalid_definition_indexes
54+
*
55+
* @param mixed $argument
56+
* @param string $exceptionMessage
57+
*/
58+
public function validates_definitionIndex($argument, $exceptionMessage)
59+
{
60+
$this->expectException(\InvalidArgumentException::class);
61+
$this->expectExceptionMessage($exceptionMessage);
62+
63+
new DefinitionHasArgumentConstraint($argument, 0);
64+
}
65+
66+
/**
67+
* @return \Generator
68+
*/
69+
public function invalid_definition_indexes()
70+
{
71+
yield [
72+
new stdClass(), 'Expected either a string or a positive integer for $argumentIndex.'
73+
];
74+
75+
yield [
76+
1.0, 'Expected either a string or a positive integer for $argumentIndex.'
77+
];
78+
79+
yield [
80+
'1', 'Unknown argument "1". Did you mean "$1"?',
81+
];
82+
83+
yield [
84+
'a', 'Unknown argument "a". Did you mean "$a"?',
85+
];
86+
87+
yield [
88+
'', 'A named argument must begin with a "$".'
89+
];
90+
}
91+
92+
/**
93+
* @test
94+
* @dataProvider indexed_arguments
95+
* @param int $argumentIndex
96+
*/
97+
public function supports_indexed_arguments($argumentIndex)
98+
{
99+
$expectedValue = 'bar';
100+
101+
$constraint = new DefinitionHasArgumentConstraint($argumentIndex, $expectedValue);
102+
$definition = new Definition(stdClass::class, array_fill(0, $argumentIndex + 1, $expectedValue));
103+
104+
self::assertTrue($constraint->evaluate($definition));
105+
self::assertSame("has an argument with index $argumentIndex with the given value", $constraint->toString());
106+
107+
$failingExpectation = $expectedValue . $expectedValue;
108+
$constraint = new DefinitionHasArgumentConstraint($argumentIndex, $failingExpectation);
109+
110+
try {
111+
$constraint->evaluate($definition);
112+
$this->fail('The expression above should throw an exception.');
113+
} catch (ExpectationFailedException $e) {
114+
self::assertStringStartsWith(
115+
sprintf(
116+
'The value of argument with index %d (\'%s\') is not equal to the expected value (\'%s\')',
117+
$argumentIndex, $expectedValue, $failingExpectation
118+
),
119+
$e->getMessage()
120+
);
121+
}
122+
}
123+
124+
/**
125+
* @return \Generator
126+
*/
127+
public function indexed_arguments()
128+
{
129+
// yield [0];
130+
yield [1];
131+
yield [2];
132+
yield [3];
133+
}
134+
135+
/**
136+
* @test
137+
* @dataProvider named_arguments
138+
* @param string $argument
139+
*/
140+
public function supports_named_arguments($argument)
141+
{
142+
$expectedValue = 'bar';
143+
144+
$constraint = new DefinitionHasArgumentConstraint($argument, $expectedValue);
145+
$definition = new Definition(stdClass::class, [
146+
$argument => $expectedValue,
147+
]);
148+
149+
self::assertTrue($constraint->evaluate($definition));
150+
self::assertSame(sprintf('has an argument named "%s" with the given value', $argument), $constraint->toString());
151+
152+
$failingExpectation = $expectedValue . $expectedValue;
153+
$constraint = new DefinitionHasArgumentConstraint($argument, $failingExpectation);
154+
155+
try {
156+
$constraint->evaluate($definition);
157+
$this->fail('The expression above should throw an exception.');
158+
} catch (ExpectationFailedException $e) {
159+
self::assertStringStartsWith(
160+
sprintf(
161+
'The value of argument named "%s" (\'%s\') is not equal to the expected value (\'%s\')',
162+
$argument, $expectedValue, $failingExpectation
163+
),
164+
$e->getMessage()
165+
);
166+
}
167+
}
168+
169+
/**
170+
* @return \Generator
171+
*/
172+
public function named_arguments()
173+
{
174+
yield ['$foo'];
175+
yield ['$bar'];
176+
yield ['$a'];
177+
}
178+
48179
}

0 commit comments

Comments
 (0)