Skip to content

Commit bc0cb32

Browse files
Stephan Wentzpl-github
authored andcommitted
fix: Fix http mock request builder assertions
1 parent ad6943a commit bc0cb32

File tree

8 files changed

+171
-84
lines changed

8 files changed

+171
-84
lines changed

composer.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,11 @@
1717
"brainbits/phpstan-rules": "^4.0",
1818
"dama/doctrine-test-bundle": "^8.2",
1919
"doctrine/dbal": "^4.2",
20-
"ergebnis/phpstan-rules": "^2.8",
20+
"ergebnis/phpstan-rules": "^2.10",
2121
"gemorroj/archive7z": "^5.7",
2222
"mikey179/vfsstream": "^1.6.12",
23-
"monolog/monolog": "^2.0 || ^3.0",
24-
"phpstan/phpstan": "^2.1.4",
23+
"monolog/monolog": "^3.0",
24+
"phpstan/phpstan": "^2.1.8",
2525
"phpstan/phpstan-phpunit": "^2.0.4",
2626
"phpstan/phpstan-symfony": "^2.0.2",
2727
"phpunit/phpunit": "^12.0.2",

phpstan.neon.dist

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ parameters:
1010
- '#Constructor in .* has parameter .* with default value#'
1111
- '#SchemaBuilder::foo\(\)#'
1212
ergebnis:
13+
noNamedArgument:
14+
enabled: false
1315
noAssignByReference:
1416
enabled: false
1517
noParameterPassedByReference:

src/HttpClientMock/CallbackHandler.php

Lines changed: 17 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -8,35 +8,28 @@
88
use Monolog\Level;
99
use Monolog\LogRecord;
1010

11-
use function class_exists;
11+
final class CallbackHandler extends AbstractProcessingHandler
12+
{
13+
/** @var callable */
14+
private $fn;
1215

13-
if (class_exists(LogRecord::class)) {
14-
/**
15-
* Callback handler for Monolog 3.x
16-
*/
17-
final class CallbackHandler extends AbstractProcessingHandler
16+
public function __construct(callable $fn, int|string|Level $level = Level::Debug, bool $bubble = true)
1817
{
19-
/** @var callable */
20-
private $fn;
18+
parent::__construct($level, $bubble);
2119

22-
public function __construct(callable $fn, int|string|Level $level = Level::Debug, bool $bubble = true)
23-
{
24-
parent::__construct($level, $bubble);
25-
26-
$this->fn = $fn;
27-
}
20+
$this->fn = $fn;
21+
}
2822

29-
public function clear(): void
30-
{
31-
}
23+
public function clear(): void
24+
{
25+
}
3226

33-
public function reset(): void
34-
{
35-
}
27+
public function reset(): void
28+
{
29+
}
3630

37-
protected function write(LogRecord $record): void
38-
{
39-
($this->fn)($record);
40-
}
31+
protected function write(LogRecord $record): void
32+
{
33+
($this->fn)($record);
4134
}
4235
}

src/HttpClientMock/HttpClientMockTrait.php

Lines changed: 58 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
use ArrayObject;
88
use Brainbits\FunctionalTestHelpers\HttpClientMock\Exception\HttpClientMockException;
99
use Monolog\Logger;
10+
use PHPUnit\Framework\Attributes\After;
11+
use PHPUnit\Framework\Attributes\Before;
1012
use PHPUnit\Framework\TestCase;
1113
use Symfony\Component\Console\Event\ConsoleErrorEvent;
1214
use Symfony\Component\Console\Event\ConsoleTerminateEvent;
@@ -22,15 +24,67 @@
2224
use function Safe\parse_url;
2325
use function sprintf;
2426
use function str_contains;
25-
use function trigger_deprecation;
27+
use function trigger_error;
2628
use function ucfirst;
2729
use function urldecode;
2830
use function vsprintf;
2931

32+
use const E_USER_DEPRECATED;
33+
3034
/** @mixin TestCase */
3135
trait HttpClientMockTrait
3236
{
33-
protected function registerNoMatchingMockRequestAsserts(
37+
/** @var list<MockRequestBuilder> */
38+
protected array $createdMockRequestBuilders = [];
39+
/** @var list<string> */
40+
protected array $mockRequestLoggerNames = ['monolog.logger'];
41+
42+
#[Before]
43+
final protected function setUpMockRequestBuilder(): void
44+
{
45+
$container = static::getContainer();
46+
$eventDispatcher = $container->get('event_dispatcher');
47+
assert($eventDispatcher instanceof EventDispatcherInterface);
48+
49+
$loggerNames = $this->mockRequestLoggerNames;
50+
51+
$this->registerNoMatchingMockRequestListeners(
52+
$eventDispatcher,
53+
...array_map(
54+
static function ($loggerName) {
55+
$logger = static::getContainer()->get($loggerName);
56+
assert($logger instanceof Logger);
57+
58+
return $logger;
59+
},
60+
$loggerNames,
61+
),
62+
);
63+
}
64+
65+
#[After]
66+
protected function assertNoFailedMockRequests(): void
67+
{
68+
$mockRequestBuilders = $this->createdMockRequestBuilders;
69+
$this->createdMockRequestBuilders = [];
70+
71+
foreach ($mockRequestBuilders as $mockRequestBuilder) {
72+
foreach ($mockRequestBuilder->getFailedAssertions() as $failedAssertion) {
73+
throw $failedAssertion;
74+
}
75+
}
76+
}
77+
78+
protected function registerNoMatchingMockRequestAsserts(): void
79+
{
80+
// legacy start - remove in 8.0.0
81+
82+
trigger_error('no matching mock request listeners are now automatically registered', E_USER_DEPRECATED);
83+
84+
// legacy end - remove in 8.0.0
85+
}
86+
87+
protected function registerNoMatchingMockRequestListeners(
3488
EventDispatcherInterface $eventDispatcher,
3589
Logger ...$loggers,
3690
): void {
@@ -130,18 +184,13 @@ protected function mockRequest(string|null $method = null, string|callable|null
130184
$stack = self::getContainer()->get(MockRequestBuilderCollection::class);
131185
assert($stack instanceof MockRequestBuilderCollection);
132186

133-
$builder = (new MockRequestBuilder())
187+
$this->createdMockRequestBuilders[] = $builder = (new MockRequestBuilder())
134188
->method($method);
135189

136190
// legacy start - remove in 8.0.0
137191

138192
if (is_string($uri) && str_contains($uri, '?')) {
139-
trigger_deprecation(
140-
'functional-test-helpers',
141-
'7.0.0',
142-
'Query parameters in uri is deprecated. Use %s instead',
143-
'queryParam()',
144-
);
193+
trigger_error('Query parameters in uri is deprecated. Use queryParam() instead', E_USER_DEPRECATED);
145194
$uriParts = parse_url($uri);
146195
$uri = ($uriParts['scheme'] ?? false ? $uriParts['scheme'] . '://' : '') .
147196
($uriParts['host'] ?? false ? $uriParts['host'] : '') .

src/HttpClientMock/MockRequestBuilder.php

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@ final class MockRequestBuilder
5151
/** @var callable|null */
5252
public mixed $onMatch = null;
5353

54+
/** @var list<Throwable> */
55+
private array $failedAssertions = [];
56+
5457
public function __construct()
5558
{
5659
$this->responses = new MockResponseCollection();
@@ -165,10 +168,20 @@ public function assertThat(callable $assert): self
165168
return $this;
166169
}
167170

171+
/** @return list<Throwable> */
172+
public function getFailedAssertions(): array
173+
{
174+
return $this->failedAssertions;
175+
}
176+
168177
public function assert(RealRequest $realRequest): void
169178
{
170179
foreach ($this->assertions as $assertion) {
171-
$assertion($realRequest, $this);
180+
try {
181+
$assertion($realRequest, $this);
182+
} catch (Throwable $e) {
183+
$this->failedAssertions[] = $e;
184+
}
172185
}
173186
}
174187

tests/HttpClientMock/HttpClientMockTraitTest.php

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,24 +11,34 @@
1111
use Brainbits\FunctionalTestHelpers\HttpClientMock\Matcher\UriParams;
1212
use Brainbits\FunctionalTestHelpers\HttpClientMock\MockRequestBuilderCollection;
1313
use Brainbits\FunctionalTestHelpers\HttpClientMock\MockRequestMatcher;
14+
use Monolog\Logger;
15+
use PHPUnit\Framework\Attributes\Before;
1416
use PHPUnit\Framework\TestCase;
1517
use Symfony\Component\DependencyInjection\Container;
18+
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
1619

1720
final class HttpClientMockTraitTest extends TestCase
1821
{
1922
use HttpClientMockTrait;
2023

2124
private static MockRequestBuilderCollection|null $collection = null;
25+
private static EventDispatcherInterface|null $dispatcher = null;
26+
private static Logger|null $logger = null;
2227

23-
public function setUp(): void
28+
#[Before(100)]
29+
public function createContainerServices(): void
2430
{
2531
self::$collection = new MockRequestBuilderCollection();
32+
self::$dispatcher = $this->createMock(EventDispatcherInterface::class);
33+
self::$logger = $this->createMock(Logger::class);
2634
}
2735

2836
public static function getContainer(): Container
2937
{
3038
$container = new Container();
3139
$container->set(MockRequestBuilderCollection::class, self::$collection);
40+
$container->set('event_dispatcher', self::$dispatcher);
41+
$container->set('monolog.logger', self::$logger);
3242

3343
return $container;
3444
}

tests/HttpClientMock/MockRequestBuilderCollectionTest.php

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@
99
use Brainbits\FunctionalTestHelpers\HttpClientMock\MockRequestMatcher;
1010
use Brainbits\FunctionalTestHelpers\HttpClientMock\MockResponseBuilder;
1111
use Brainbits\FunctionalTestHelpers\HttpClientMock\RealRequest;
12-
use PHPUnit\Framework\AssertionFailedError;
1312
use PHPUnit\Framework\Attributes\CoversClass;
1413
use PHPUnit\Framework\Attributes\DataProvider;
14+
use PHPUnit\Framework\ExpectationFailedException;
1515
use PHPUnit\Framework\TestCase;
1616

1717
#[CoversClass(MockRequestBuilder::class)]
@@ -173,68 +173,74 @@ public function testAssertContent(): void
173173
{
174174
$collection = new MockRequestBuilderCollection();
175175
$collection->addMockRequestBuilder(
176-
(new MockRequestBuilder())
176+
$builder = (new MockRequestBuilder())
177177
->assertContent(function (string $content): void {
178178
$this->assertSame('this is content', $content);
179179
})
180180
->willRespond(new MockResponseBuilder()),
181181
);
182182

183183
$collection('GET', '/query', ['body' => 'this is content']);
184+
185+
$this->assertSame([], $builder->getFailedAssertions());
184186
}
185187

186188
public function testAssertContentFails(): void
187189
{
188190
$collection = new MockRequestBuilderCollection();
189191
$collection->addMockRequestBuilder(
190-
(new MockRequestBuilder())
192+
$builder = (new MockRequestBuilder())
191193
->assertContent(function (string $content): void {
192194
$this->assertSame('this is content', $content);
193195
})
194196
->willRespond(new MockResponseBuilder()),
195197
);
196198

197-
try {
198-
$collection('GET', '/query', ['body' => 'does-not-match']);
199-
} catch (AssertionFailedError) {
200-
return;
201-
}
199+
$collection('GET', '/query', ['body' => 'does-not-match']);
202200

203-
$this->fail('Expected assertion was not thrown');
201+
$failedAssertions = $builder->getFailedAssertions();
202+
$this->assertCount(1, $failedAssertions);
203+
$this->assertInstanceOf(
204+
ExpectationFailedException::class,
205+
$failedAssertions[0],
206+
);
204207
}
205208

206209
public function testAssertThat(): void
207210
{
208211
$collection = new MockRequestBuilderCollection();
209212
$collection->addMockRequestBuilder(
210-
(new MockRequestBuilder())
213+
$builder = (new MockRequestBuilder())
211214
->assertThat(function (RealRequest $realRequest): void {
212215
$this->assertSame('this is content', $realRequest->getContent());
213216
})
214217
->willRespond(new MockResponseBuilder()),
215218
);
216219

217220
$collection('GET', '/query', ['body' => 'this is content']);
221+
222+
$this->assertSame([], $builder->getFailedAssertions());
218223
}
219224

220225
public function testAssertThatFails(): void
221226
{
222227
$collection = new MockRequestBuilderCollection();
223228
$collection->addMockRequestBuilder(
224-
(new MockRequestBuilder())
229+
$builder = (new MockRequestBuilder())
225230
->assertThat(function (RealRequest $realRequest): void {
226231
$this->assertSame('this is content', $realRequest->getContent());
227232
})
228233
->willRespond(new MockResponseBuilder()),
229234
);
230235

231-
try {
232-
$collection('GET', '/query', ['body' => 'does-not-match']);
233-
} catch (AssertionFailedError) {
234-
return;
235-
}
236+
$collection('GET', '/query', ['body' => 'does-not-match']);
236237

237-
$this->fail('Expected assertion was not thrown');
238+
$failedAssertions = $builder->getFailedAssertions();
239+
$this->assertCount(1, $failedAssertions);
240+
$this->assertInstanceOf(
241+
ExpectationFailedException::class,
242+
$failedAssertions[0],
243+
);
238244
}
239245

240246
/** @return mixed[] */

0 commit comments

Comments
 (0)