Skip to content

Commit e1588d4

Browse files
JasonBenettclaude
andauthored
fix: use urlPath for query parameter matching in WireMock requests (#2)
When queryParameters are specified in request matchers, WireMock requires 'urlPath' instead of 'url' to match the path separately from query params. This fix: - Adds determineUrlKey() method to automatically select correct URL matcher - Eliminates code duplication across haveHttpStubFor, seeHttpRequest, dontSeeHttpRequest - Respects explicit user overrides for urlPath/urlPattern - Adds comprehensive unit tests with data provider using generators - Updates documentation to replace docker-compose with docker run commands 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude <noreply@anthropic.com>
1 parent 4c2ec0e commit e1588d4

File tree

5 files changed

+132
-20
lines changed

5 files changed

+132
-20
lines changed

CLAUDE.md

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,10 @@ composer cs-fix # Fix code style issues
6262

6363
### WireMock Server
6464
```bash
65-
docker-compose up -d # Start WireMock server on port 8080
66-
docker-compose down # Stop WireMock server
67-
docker-compose logs wiremock # View WireMock logs
65+
docker run -d -p 8080:8080 wiremock/wiremock:latest # Start WireMock server on port 8080
66+
docker ps | grep wiremock # Check if WireMock is running
67+
docker logs <container-id> # View WireMock logs
68+
docker stop <container-id> # Stop WireMock server
6869
```
6970

7071
## Development Workflow & Conventions
@@ -249,13 +250,13 @@ modules:
249250

250251
1. Start WireMock server:
251252
```bash
252-
docker-compose up -d
253+
docker run -d -p 8080:8080 wiremock/wiremock:latest
253254
```
254255

255-
2. Wait for WireMock to be healthy:
256+
2. Wait for WireMock to be ready:
256257
```bash
257-
docker-compose ps
258-
# Should show "healthy" status
258+
curl http://localhost:8080/__admin/health
259+
# Should return: {"status":"OK"}
259260
```
260261

261262
3. Run unit tests (don't require WireMock):
@@ -277,7 +278,7 @@ modules:
277278

278279
- Use `grabAllRequests()` to see all requests made
279280
- Use `grabUnmatchedRequests()` to see requests that didn't match any stub
280-
- Check WireMock logs: `docker-compose logs wiremock`
281+
- Check WireMock logs: `docker logs <container-id>`
281282
- Near-miss analysis is automatically included in verification error messages
282283

283284
## Common Patterns

CONTRIBUTING.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ composer install
5353
### Start WireMock Server
5454

5555
```bash
56-
docker-compose up -d
56+
docker run -d -p 8080:8080 wiremock/wiremock:latest
5757
```
5858

5959
Verify WireMock is running:
@@ -93,7 +93,7 @@ composer test
9393
Functional tests require WireMock to be running:
9494

9595
```bash
96-
docker-compose up -d
96+
docker run -d -p 8080:8080 wiremock/wiremock:latest
9797
composer test:functional
9898
```
9999

README.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -473,22 +473,22 @@ class ShoppingCartCest
473473

474474
## Local Development
475475

476-
### Using Docker Compose
476+
### Using Docker
477477

478-
The project includes a `docker-compose.yml` for easy local development:
478+
You can run WireMock using Docker:
479479

480480
```bash
481481
# Start WireMock
482-
docker-compose up -d
482+
docker run -d -p 8080:8080 wiremock/wiremock:latest
483483
484484
# Check status
485-
docker-compose ps
485+
docker ps | grep wiremock
486486
487487
# View logs
488-
docker-compose logs -f wiremock
488+
docker logs <container-id>
489489
490490
# Stop WireMock
491-
docker-compose down
491+
docker stop <container-id>
492492
```
493493

494494
### Running Tests
@@ -501,7 +501,7 @@ composer install
501501
composer test
502502
503503
# Start WireMock
504-
docker-compose up -d
504+
docker run -d -p 8080:8080 wiremock/wiremock:latest
505505
506506
# Build Codeception support classes
507507
vendor/bin/codecept build

src/JasonBenett/CodeceptionModuleWiremock/Module/Wiremock.php

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -138,10 +138,12 @@ public function haveHttpStubFor(
138138
array $headers = [],
139139
array $requestMatchers = [],
140140
): string {
141+
$urlKey = $this->determineUrlKey($requestMatchers);
142+
141143
$mapping = [
142144
'request' => array_merge([
143145
'method' => strtoupper($method),
144-
'url' => $url,
146+
$urlKey => $url,
145147
], $requestMatchers),
146148
'response' => [
147149
'status' => $status,
@@ -187,9 +189,11 @@ public function seeHttpRequest(
187189
string $url,
188190
array $additionalMatchers = [],
189191
): void {
192+
$urlKey = $this->determineUrlKey($additionalMatchers);
193+
190194
$pattern = array_merge([
191195
'method' => strtoupper($method),
192-
'url' => $url,
196+
$urlKey => $url,
193197
], $additionalMatchers);
194198

195199
$count = $this->grabRequestCount($pattern);
@@ -227,9 +231,11 @@ public function dontSeeHttpRequest(
227231
string $url,
228232
array $additionalMatchers = [],
229233
): void {
234+
$urlKey = $this->determineUrlKey($additionalMatchers);
235+
230236
$pattern = array_merge([
231237
'method' => strtoupper($method),
232-
'url' => $url,
238+
$urlKey => $url,
233239
], $additionalMatchers);
234240

235241
$count = $this->grabRequestCount($pattern);
@@ -608,4 +614,30 @@ private function initStreamFactory(): void
608614

609615
$this->streamFactory = $streamFactory;
610616
}
617+
618+
/**
619+
* Determine whether to use 'url' or 'urlPath' based on request matchers
620+
*
621+
* When queryParameters are specified in request matchers, WireMock requires
622+
* 'urlPath' instead of 'url' to match the path separately from query params.
623+
*
624+
* @param array<string, mixed> $matchers Request matcher array
625+
*
626+
* @return string Either 'url' or 'urlPath'
627+
*/
628+
private function determineUrlKey(array $matchers): string
629+
{
630+
// If queryParameters are present and user hasn't explicitly set urlPath/urlPattern,
631+
// use 'urlPath' for path-only matching (allowing separate query param matching)
632+
if (
633+
isset($matchers['queryParameters'])
634+
&& !isset($matchers['urlPath'])
635+
&& !isset($matchers['urlPattern'])
636+
) {
637+
return 'urlPath';
638+
}
639+
640+
// Default to 'url' for exact URL matching
641+
return 'url';
642+
}
611643
}

tests/unit/Codeception/Module/WiremockTest.php

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
use Codeception\Lib\ModuleContainer;
88
use Codeception\Test\Unit;
9+
use Generator;
910
use GuzzleHttp\Client;
1011
use GuzzleHttp\Handler\MockHandler;
1112
use GuzzleHttp\HandlerStack;
@@ -270,4 +271,82 @@ public function testSendClearRequestsCallsClearEndpoint(): void
270271
// Should not throw exception
271272
$this->assertTrue(true);
272273
}
274+
275+
/**
276+
* Test determineUrlKey method for proper url vs urlPath selection
277+
*
278+
* @dataProvider urlKeyDataProvider
279+
*/
280+
public function testDetermineUrlKey(array $matchers, string $expectedKey): void
281+
{
282+
$reflection = new ReflectionClass($this->module);
283+
$method = $reflection->getMethod('determineUrlKey');
284+
285+
$result = $method->invoke($this->module, $matchers);
286+
287+
$this->assertSame($expectedKey, $result);
288+
}
289+
290+
/**
291+
* Data provider for testDetermineUrlKey
292+
*
293+
* @return Generator<string, array{matchers: array<string, mixed>, expectedKey: string}>
294+
*/
295+
public static function urlKeyDataProvider(): Generator
296+
{
297+
yield 'no matchers - uses url' => [
298+
'matchers' => [],
299+
'expectedKey' => 'url',
300+
];
301+
302+
yield 'only method matcher - uses url' => [
303+
'matchers' => ['method' => 'GET'],
304+
'expectedKey' => 'url',
305+
];
306+
307+
yield 'with queryParameters - uses urlPath' => [
308+
'matchers' => [
309+
'queryParameters' => ['q' => ['equalTo' => 'London']],
310+
],
311+
'expectedKey' => 'urlPath',
312+
];
313+
314+
yield 'with queryParameters and other matchers - uses urlPath' => [
315+
'matchers' => [
316+
'queryParameters' => ['q' => ['equalTo' => 'London']],
317+
'headers' => ['Content-Type' => ['equalTo' => 'application/json']],
318+
],
319+
'expectedKey' => 'urlPath',
320+
];
321+
322+
yield 'explicit urlPath overrides - uses url (respects user choice)' => [
323+
'matchers' => [
324+
'queryParameters' => ['q' => ['equalTo' => 'London']],
325+
'urlPath' => '/api/weather',
326+
],
327+
'expectedKey' => 'url',
328+
];
329+
330+
yield 'explicit urlPattern overrides - uses url (respects user choice)' => [
331+
'matchers' => [
332+
'queryParameters' => ['q' => ['equalTo' => 'London']],
333+
'urlPattern' => '/api/.*',
334+
],
335+
'expectedKey' => 'url',
336+
];
337+
338+
yield 'only bodyPatterns - uses url' => [
339+
'matchers' => [
340+
'bodyPatterns' => [['equalToJson' => '{"name":"test"}']],
341+
],
342+
'expectedKey' => 'url',
343+
];
344+
345+
yield 'only headers - uses url' => [
346+
'matchers' => [
347+
'headers' => ['Authorization' => ['matches' => 'Bearer .*']],
348+
],
349+
'expectedKey' => 'url',
350+
];
351+
}
273352
}

0 commit comments

Comments
 (0)