Skip to content

Commit a23b1be

Browse files
committed
Add conditional responses to mock client
Following discussions at #3 this adds a on method taking a RequestMatcher and then either a Response, an Exception or a callable. This allows for mock clients to not depend on the order the responses/exceptions are mocked. It also allows for responses to depend on some content in the request, avoiding duplication in some cases.
1 parent d41093e commit a23b1be

File tree

2 files changed

+105
-1
lines changed

2 files changed

+105
-1
lines changed

spec/ClientSpec.php

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use Http\Client\HttpAsyncClient;
66
use Http\Client\HttpClient;
7+
use Http\Message\RequestMatcher;
78
use Http\Message\ResponseFactory;
89
use Http\Mock\Client;
910
use Psr\Http\Message\RequestInterface;
@@ -90,7 +91,8 @@ function it_reset(
9091
RequestInterface $request,
9192
ResponseInterface $response,
9293
ResponseInterface $newResponse
93-
) {
94+
)
95+
{
9496
$this->addResponse($response);
9597
$this->setDefaultResponse($response);
9698
$this->addException(new \Exception());
@@ -106,4 +108,53 @@ function it_reset(
106108

107109
$this->getRequests()->shouldReturn([]);
108110
}
111+
function it_returns_response_if_request_matcher_matches(
112+
RequestMatcher $matcher,
113+
RequestInterface $request,
114+
ResponseInterface $response
115+
) {
116+
$matcher->matches($request)->willReturn(true);
117+
$this->on($matcher, $response);
118+
$this->sendRequest($request)->shouldReturn($response);
119+
}
120+
121+
function it_throws_exception_if_request_matcher_matches(
122+
RequestMatcher $matcher,
123+
RequestInterface $request
124+
) {
125+
$matcher->matches($request)->willReturn(true);
126+
$this->on($matcher, new \Exception());
127+
$this->shouldThrow('Exception')->duringSendRequest($request);
128+
}
129+
130+
function it_skips_conditional_response_if_matcher_returns_false(
131+
RequestMatcher $matcher,
132+
RequestInterface $request,
133+
ResponseInterface $expectedResponse,
134+
ResponseInterface $skippedResponse
135+
)
136+
{
137+
$matcher->matches($request)->willReturn(false);
138+
$this->on($matcher, $skippedResponse);
139+
$this->addResponse($expectedResponse);
140+
$this->sendRequest($request)->shouldReturn($expectedResponse);
141+
}
142+
143+
function it_calls_callable_with_request_as_argument_when_matcher_returns_true(
144+
RequestMatcher $matcher,
145+
RequestInterface $request,
146+
ResponseInterface $response
147+
)
148+
{
149+
$matcher->matches($request)->willReturn(true);
150+
151+
$this->on(
152+
$matcher,
153+
function(RequestInterface $request) use ($response) {
154+
return $response->getWrappedObject();
155+
}
156+
);
157+
158+
$this->sendRequest($request)->shouldReturn($response);
159+
}
109160
}

src/Client.php

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use Http\Client\HttpAsyncClient;
99
use Http\Client\HttpClient;
1010
use Http\Discovery\MessageFactoryDiscovery;
11+
use Http\Message\RequestMatcher;
1112
use Http\Message\ResponseFactory;
1213
use Psr\Http\Message\RequestInterface;
1314
use Psr\Http\Message\ResponseInterface;
@@ -30,6 +31,11 @@ class Client implements HttpClient, HttpAsyncClient
3031
*/
3132
private $responseFactory;
3233

34+
/**
35+
* @var array
36+
*/
37+
private $conditionalResults = [];
38+
3339
/**
3440
* @var RequestInterface[]
3541
*/
@@ -67,6 +73,19 @@ public function doSendRequest(RequestInterface $request)
6773
{
6874
$this->requests[] = $request;
6975

76+
foreach ($this->conditionalResults as $result) {
77+
$matcher = $result['matcher'];
78+
$callable = $result['callable'];
79+
80+
/**
81+
* @var RequestMatcher $matcher
82+
* @var ResponseInterface|Exception $result
83+
*/
84+
if ($matcher->matches($request)) {
85+
return $callable($request);
86+
}
87+
}
88+
7089
if (count($this->exceptions) > 0) {
7190
throw array_shift($this->exceptions);
7291
}
@@ -87,6 +106,40 @@ public function doSendRequest(RequestInterface $request)
87106
return $this->responseFactory->createResponse();
88107
}
89108

109+
/**
110+
* @param RequestMatcher $requestMatcher
111+
* @param ResponseInterface|\Exception|callable $result
112+
*/
113+
public function on(RequestMatcher $requestMatcher, $result)
114+
{
115+
$callable = null;
116+
117+
switch (true) {
118+
case is_callable($result):
119+
$callable = $result;
120+
121+
break;
122+
case $result instanceof ResponseInterface:
123+
$callable = function () use ($result) {
124+
return $result;
125+
};
126+
127+
break;
128+
case $result instanceof \Exception:
129+
$callable = function () use ($result) {
130+
throw $result;
131+
};
132+
133+
break;
134+
default:
135+
throw new \InvalidArgumentException('Result must be either a response, an exception, or a callable');
136+
}
137+
$this->conditionalResults[] = [
138+
'matcher' => $requestMatcher,
139+
'callable' => $callable,
140+
];
141+
}
142+
90143
/**
91144
* Adds an exception that will be thrown.
92145
*/

0 commit comments

Comments
 (0)