Skip to content

Commit 5b2a391

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 28195af commit 5b2a391

File tree

2 files changed

+94
-0
lines changed

2 files changed

+94
-0
lines changed

spec/ClientSpec.php

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace spec\Http\Mock;
44

5+
use Http\Message\RequestMatcher;
56
use Http\Message\ResponseFactory;
67
use Psr\Http\Message\RequestInterface;
78
use Psr\Http\Message\ResponseInterface;
@@ -73,4 +74,54 @@ function it_returns_the_last_request(RequestInterface $request)
7374

7475
$this->getLastRequest()->shouldReturn($request);
7576
}
77+
78+
function it_returns_response_if_request_matcher_matches(
79+
RequestMatcher $matcher,
80+
RequestInterface $request,
81+
ResponseInterface $response
82+
) {
83+
$matcher->matches($request)->willReturn(true);
84+
$this->on($matcher, $response);
85+
$this->sendRequest($request)->shouldReturn($response);
86+
}
87+
88+
function it_throws_exception_if_request_matcher_matches(
89+
RequestMatcher $matcher,
90+
RequestInterface $request
91+
) {
92+
$matcher->matches($request)->willReturn(true);
93+
$this->on($matcher, new \Exception());
94+
$this->shouldThrow('Exception')->duringSendRequest($request);
95+
}
96+
97+
function it_skips_conditional_response_if_matcher_returns_false(
98+
RequestMatcher $matcher,
99+
RequestInterface $request,
100+
ResponseInterface $expectedResponse,
101+
ResponseInterface $skippedResponse
102+
)
103+
{
104+
$matcher->matches($request)->willReturn(false);
105+
$this->on($matcher, $skippedResponse);
106+
$this->addResponse($expectedResponse);
107+
$this->sendRequest($request)->shouldReturn($expectedResponse);
108+
}
109+
110+
function it_calls_callable_with_request_as_argument_when_matcher_returns_true(
111+
RequestMatcher $matcher,
112+
RequestInterface $request,
113+
ResponseInterface $response
114+
)
115+
{
116+
$matcher->matches($request)->willReturn(true);
117+
118+
$this->on(
119+
$matcher,
120+
function(RequestInterface $request) use ($response) {
121+
return $response->getWrappedObject();
122+
}
123+
);
124+
125+
$this->sendRequest($request)->shouldReturn($response);
126+
}
76127
}

src/Client.php

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

33+
/**
34+
* @var array
35+
*/
36+
private $conditionalResults = [];
37+
3238
/**
3339
* @var RequestInterface[]
3440
*/
@@ -69,6 +75,17 @@ public function sendRequest(RequestInterface $request)
6975
{
7076
$this->requests[] = $request;
7177

78+
foreach ($this->conditionalResults as ['matcher' => $matcher, 'callable' => $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+
7289
if (count($this->exceptions) > 0) {
7390
throw array_shift($this->exceptions);
7491
}
@@ -89,6 +106,32 @@ public function sendRequest(RequestInterface $request)
89106
return $this->responseFactory->createResponse();
90107
}
91108

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

0 commit comments

Comments
 (0)