Skip to content

Commit d1c5d2e

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 d1c5d2e

File tree

2 files changed

+104
-0
lines changed

2 files changed

+104
-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: 53 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,19 @@ public function sendRequest(RequestInterface $request)
6975
{
7076
$this->requests[] = $request;
7177

78+
foreach ($this->conditionalResults as $result) {
79+
$matcher = $result['matcher'];
80+
$callable = $result['callable'];
81+
82+
/**
83+
* @var RequestMatcher $matcher
84+
* @var ResponseInterface|Exception $result
85+
*/
86+
if ($matcher->matches($request)) {
87+
return $callable($request);
88+
}
89+
}
90+
7291
if (count($this->exceptions) > 0) {
7392
throw array_shift($this->exceptions);
7493
}
@@ -89,6 +108,40 @@ public function sendRequest(RequestInterface $request)
89108
return $this->responseFactory->createResponse();
90109
}
91110

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

0 commit comments

Comments
 (0)