mirror of
https://github.com/Neur0toxine/pock.git
synced 2024-12-01 09:26:02 +03:00
replyWithFactory
and replyWithCallback
methods
This commit is contained in:
parent
26d707000c
commit
60003ef9cc
@ -120,7 +120,7 @@ In order to use unsupported serializer you should create a decorator which imple
|
||||
- [ ] Regexp matchers for body, query and path.
|
||||
- [x] Separate `UniversalMockException` into several exceptions (`PockClientException`, `PockNetworkException`, etc).
|
||||
- [x] Add methods for easier throwing of exceptions listed in previous entry.
|
||||
- [ ] `replyCallback` - reply using specified callback.
|
||||
- [ ] `replyFactory` - reply using specified response factory (provide corresponding interface).
|
||||
- [x] `replyWithCallback` - reply using specified callback.
|
||||
- [x] `replyWithFactory` - reply using specified response factory (provide corresponding interface).
|
||||
- [ ] Compare XML bodies using `DOMDocument`, fallback to text comparison in case of problems.
|
||||
- [ ] Document everything (with examples if it’s feasible).
|
||||
|
@ -81,6 +81,18 @@ class Client implements ClientInterface, HttpClient, HttpAsyncClient
|
||||
return new HttpFulfilledPromise($mock->getResponse());
|
||||
}
|
||||
|
||||
if (null !== $mock->getReplyFactory()) {
|
||||
$mock->registerHit();
|
||||
|
||||
try {
|
||||
return new HttpFulfilledPromise(
|
||||
$mock->getReplyFactory()->createReply($request, new PockResponseBuilder())
|
||||
);
|
||||
} catch (Throwable $throwable) {
|
||||
return new HttpRejectedPromise($throwable);
|
||||
}
|
||||
}
|
||||
|
||||
$throwable = $mock->getThrowable($request);
|
||||
|
||||
if (null !== $throwable) {
|
||||
@ -94,13 +106,23 @@ class Client implements ClientInterface, HttpClient, HttpAsyncClient
|
||||
}
|
||||
|
||||
if (null !== $this->fallbackClient) {
|
||||
try {
|
||||
return new HttpFulfilledPromise($this->fallbackClient->sendRequest($request));
|
||||
} catch (Throwable $throwable) {
|
||||
return new HttpRejectedPromise($throwable);
|
||||
}
|
||||
return $this->replyWithFallbackClient($request);
|
||||
}
|
||||
|
||||
throw new UnsupportedRequestException();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Psr\Http\Message\RequestInterface $request
|
||||
*
|
||||
* @return \Http\Promise\Promise
|
||||
*/
|
||||
protected function replyWithFallbackClient(RequestInterface $request): Promise
|
||||
{
|
||||
try {
|
||||
return new HttpFulfilledPromise($this->fallbackClient->sendRequest($request)); // @phpstan-ignore-line
|
||||
} catch (Throwable $throwable) {
|
||||
return new HttpRejectedPromise($throwable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
44
src/Factory/CallbackReplyFactory.php
Normal file
44
src/Factory/CallbackReplyFactory.php
Normal file
@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP 7.3
|
||||
*
|
||||
* @category CallbackReplyFactory
|
||||
* @package Pock\Factory
|
||||
*/
|
||||
|
||||
namespace Pock\Factory;
|
||||
|
||||
use Pock\PockResponseBuilder;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
/**
|
||||
* Class CallbackReplyFactory
|
||||
*
|
||||
* @category CallbackReplyFactory
|
||||
* @package Pock\Factory
|
||||
*/
|
||||
class CallbackReplyFactory implements ReplyFactoryInterface
|
||||
{
|
||||
/** @var callable */
|
||||
private $callback;
|
||||
|
||||
/**
|
||||
* CallbackReplyFactory constructor.
|
||||
*
|
||||
* @param callable $callback
|
||||
*/
|
||||
public function __construct(callable $callback)
|
||||
{
|
||||
$this->callback = $callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function createReply(RequestInterface $request, PockResponseBuilder $responseBuilder): ResponseInterface
|
||||
{
|
||||
return call_user_func($this->callback, $request, $responseBuilder);
|
||||
}
|
||||
}
|
38
src/Factory/ReplyFactoryInterface.php
Normal file
38
src/Factory/ReplyFactoryInterface.php
Normal file
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP 7.3
|
||||
*
|
||||
* @category ReplyFactoryInterface
|
||||
* @package Pock\Factory
|
||||
*/
|
||||
|
||||
namespace Pock\Factory;
|
||||
|
||||
use Pock\PockResponseBuilder;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
/**
|
||||
* Interface ReplyFactoryInterface
|
||||
*
|
||||
* @category ReplyFactoryInterface
|
||||
* @package Pock\Factory
|
||||
*/
|
||||
interface ReplyFactoryInterface
|
||||
{
|
||||
/**
|
||||
* Reply to the specified request.
|
||||
*
|
||||
* If this method throws any exception, it will be treated as with the `PockBuilder::throwException call`.
|
||||
*
|
||||
* @see \Pock\PockBuilder::throwException()
|
||||
*
|
||||
* @param \Psr\Http\Message\RequestInterface $request
|
||||
* @param \Pock\PockResponseBuilder $responseBuilder
|
||||
*
|
||||
* @return \Psr\Http\Message\ResponseInterface
|
||||
* @throws \Throwable
|
||||
*/
|
||||
public function createReply(RequestInterface $request, PockResponseBuilder $responseBuilder): ResponseInterface;
|
||||
}
|
15
src/Mock.php
15
src/Mock.php
@ -11,6 +11,7 @@ namespace Pock;
|
||||
|
||||
use Pock\Exception\PockNetworkException;
|
||||
use Pock\Exception\PockRequestException;
|
||||
use Pock\Factory\ReplyFactoryInterface;
|
||||
use Pock\Matchers\RequestMatcherInterface;
|
||||
use Psr\Http\Client\NetworkExceptionInterface;
|
||||
use Psr\Http\Client\RequestExceptionInterface;
|
||||
@ -29,6 +30,9 @@ class Mock implements MockInterface
|
||||
/** @var \Pock\Matchers\RequestMatcherInterface */
|
||||
private $matcher;
|
||||
|
||||
/** @var \Pock\Factory\ReplyFactoryInterface|null */
|
||||
private $replyFactory;
|
||||
|
||||
/** @var \Psr\Http\Message\ResponseInterface|null */
|
||||
private $response;
|
||||
|
||||
@ -48,6 +52,7 @@ class Mock implements MockInterface
|
||||
* Mock constructor.
|
||||
*
|
||||
* @param \Pock\Matchers\RequestMatcherInterface $matcher
|
||||
* @param \Pock\Factory\ReplyFactoryInterface|null $replyFactory
|
||||
* @param \Psr\Http\Message\ResponseInterface|null $response
|
||||
* @param \Throwable|null $throwable
|
||||
* @param int $maxHits
|
||||
@ -55,12 +60,14 @@ class Mock implements MockInterface
|
||||
*/
|
||||
public function __construct(
|
||||
RequestMatcherInterface $matcher,
|
||||
?ReplyFactoryInterface $replyFactory,
|
||||
?ResponseInterface $response,
|
||||
?Throwable $throwable,
|
||||
int $maxHits,
|
||||
int $matchAt
|
||||
) {
|
||||
$this->matcher = $matcher;
|
||||
$this->replyFactory = $replyFactory;
|
||||
$this->response = $response;
|
||||
$this->throwable = $throwable;
|
||||
$this->matchAt = $matchAt;
|
||||
@ -128,6 +135,14 @@ class Mock implements MockInterface
|
||||
return $this->response;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getReplyFactory(): ?ReplyFactoryInterface
|
||||
{
|
||||
return $this->replyFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
@ -9,6 +9,7 @@
|
||||
|
||||
namespace Pock;
|
||||
|
||||
use Pock\Factory\ReplyFactoryInterface;
|
||||
use Pock\Matchers\RequestMatcherInterface;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
@ -53,6 +54,13 @@ interface MockInterface
|
||||
*/
|
||||
public function getResponse(): ?ResponseInterface;
|
||||
|
||||
/**
|
||||
* Returns reply factory which should be used to form the mocked response.
|
||||
*
|
||||
* @return \Pock\Factory\ReplyFactoryInterface|null
|
||||
*/
|
||||
public function getReplyFactory(): ?ReplyFactoryInterface;
|
||||
|
||||
/**
|
||||
* Returns the throwable which will be thrown as mock data.
|
||||
*
|
||||
|
@ -15,6 +15,8 @@ use Pock\Enum\RequestScheme;
|
||||
use Pock\Exception\PockClientException;
|
||||
use Pock\Exception\PockNetworkException;
|
||||
use Pock\Exception\PockRequestException;
|
||||
use Pock\Factory\CallbackReplyFactory;
|
||||
use Pock\Factory\ReplyFactoryInterface;
|
||||
use Pock\Matchers\AnyRequestMatcher;
|
||||
use Pock\Matchers\BodyMatcher;
|
||||
use Pock\Matchers\CallbackRequestMatcher;
|
||||
@ -62,6 +64,9 @@ class PockBuilder
|
||||
/** @var \Pock\PockResponseBuilder|null */
|
||||
private $responseBuilder;
|
||||
|
||||
/** @var ReplyFactoryInterface|null */
|
||||
private $replyFactory;
|
||||
|
||||
/** @var \Throwable|null */
|
||||
private $throwable;
|
||||
|
||||
@ -454,6 +459,31 @@ class PockBuilder
|
||||
return $this->responseBuilder->withStatusCode($statusCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct the response during request execution using provided ReplytFactoryInterface implementation.
|
||||
*
|
||||
* @param \Pock\Factory\ReplyFactoryInterface $factory
|
||||
* @see ReplyFactoryInterface
|
||||
*/
|
||||
public function replyWithFactory(ReplyFactoryInterface $factory): void
|
||||
{
|
||||
$this->replyFactory = $factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct the response during request execution using provided callback.
|
||||
*
|
||||
* Callback should receive the same parameters as in the `ReplyFactoryInterface::createReply` method.
|
||||
*
|
||||
* @see ReplyFactoryInterface::createReply()
|
||||
*
|
||||
* @param callable $callback
|
||||
*/
|
||||
public function replyWithCallback(callable $callback): void
|
||||
{
|
||||
$this->replyWithFactory(new CallbackReplyFactory($callback));
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the builder.
|
||||
*
|
||||
@ -462,6 +492,7 @@ class PockBuilder
|
||||
public function reset(): self
|
||||
{
|
||||
$this->matcher = new MultipleMatcher();
|
||||
$this->replyFactory = null;
|
||||
$this->responseBuilder = null;
|
||||
$this->throwable = null;
|
||||
$this->maxHits = 1;
|
||||
@ -495,7 +526,7 @@ class PockBuilder
|
||||
|
||||
private function closePrevious(): void
|
||||
{
|
||||
if (null !== $this->responseBuilder || null !== $this->throwable) {
|
||||
if (null !== $this->responseBuilder || null !== $this->replyFactory || null !== $this->throwable) {
|
||||
if (0 === count($this->matcher)) {
|
||||
$this->matcher->addMatcher(new AnyRequestMatcher());
|
||||
}
|
||||
@ -508,12 +539,14 @@ class PockBuilder
|
||||
|
||||
$this->mocks[] = new Mock(
|
||||
$this->matcher,
|
||||
$this->replyFactory,
|
||||
$response,
|
||||
$this->throwable,
|
||||
$this->maxHits,
|
||||
$this->matchAt
|
||||
);
|
||||
$this->matcher = new MultipleMatcher();
|
||||
$this->replyFactory = null;
|
||||
$this->responseBuilder = null;
|
||||
$this->throwable = null;
|
||||
$this->maxHits = 1;
|
||||
|
@ -12,13 +12,18 @@ namespace Pock\Tests;
|
||||
use Pock\Enum\RequestMethod;
|
||||
use Pock\Enum\RequestScheme;
|
||||
use Pock\Exception\UnsupportedRequestException;
|
||||
use Pock\Factory\ReplyFactoryInterface;
|
||||
use Pock\PockBuilder;
|
||||
use Pock\PockResponseBuilder;
|
||||
use Pock\TestUtils\PockTestCase;
|
||||
use Pock\TestUtils\SimpleObject;
|
||||
use Pock\TestUtils\TestReplyFactory;
|
||||
use Psr\Http\Client\ClientExceptionInterface;
|
||||
use Psr\Http\Client\NetworkExceptionInterface;
|
||||
use Psr\Http\Client\RequestExceptionInterface;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* Class PockBuilderTest
|
||||
@ -587,4 +592,61 @@ EOF;
|
||||
self::assertEquals(1 === $i ? 200 : (4 === $i ? 201 : 400), $response->getStatusCode());
|
||||
}
|
||||
}
|
||||
|
||||
public function testReplyWithFactory(): void
|
||||
{
|
||||
$builder = new PockBuilder();
|
||||
$builder->matchMethod(RequestMethod::GET)
|
||||
->matchUri(self::TEST_URI)
|
||||
->always()
|
||||
->replyWithFactory(new TestReplyFactory());
|
||||
|
||||
for ($i = 0; $i < 5; $i++) {
|
||||
$response = $builder->getClient()->sendRequest(
|
||||
self::getPsr17Factory()->createRequest(RequestMethod::GET, self::TEST_URI)
|
||||
);
|
||||
|
||||
self::assertEquals(200, $response->getStatusCode());
|
||||
self::assertEquals('Request #' . ($i + 1), $response->getBody()->getContents());
|
||||
}
|
||||
}
|
||||
|
||||
public function testReplyWithCallback(): void
|
||||
{
|
||||
$builder = new PockBuilder();
|
||||
$builder->matchMethod(RequestMethod::GET)
|
||||
->matchUri(self::TEST_URI)
|
||||
->always()
|
||||
->replyWithCallback(static function (RequestInterface $request, PockResponseBuilder $responseBuilder) {
|
||||
return $responseBuilder->withStatusCode(200)
|
||||
->withBody(self::TEST_URI)
|
||||
->getResponse();
|
||||
});
|
||||
|
||||
$response = $builder->getClient()->sendRequest(
|
||||
self::getPsr17Factory()->createRequest(RequestMethod::GET, self::TEST_URI)
|
||||
);
|
||||
|
||||
self::assertEquals(200, $response->getStatusCode());
|
||||
self::assertEquals(self::TEST_URI, $response->getBody()->getContents());
|
||||
}
|
||||
|
||||
public function testReplyWithCallbackException(): void
|
||||
{
|
||||
$this->expectException(RuntimeException::class);
|
||||
$this->expectExceptionMessage('Exception from the callback');
|
||||
|
||||
$builder = new PockBuilder();
|
||||
$builder->matchMethod(RequestMethod::GET)
|
||||
->matchUri(self::TEST_URI)
|
||||
->always()
|
||||
->replyWithCallback(static function (RequestInterface $request, PockResponseBuilder $responseBuilder) {
|
||||
throw new RuntimeException('Exception from the callback');
|
||||
});
|
||||
|
||||
$builder->getClient()->sendRequest(self::getPsr17Factory()->createRequest(
|
||||
RequestMethod::GET,
|
||||
self::TEST_URI
|
||||
));
|
||||
}
|
||||
}
|
||||
|
37
tests/utils/TestReplyFactory.php
Normal file
37
tests/utils/TestReplyFactory.php
Normal file
@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP 7.3
|
||||
*
|
||||
* @category TestReplyFactory
|
||||
* @package Pock\TestUtils
|
||||
*/
|
||||
|
||||
namespace Pock\TestUtils;
|
||||
|
||||
use Pock\Factory\ReplyFactoryInterface;
|
||||
use Pock\PockResponseBuilder;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
/**
|
||||
* Class TestReplyFactory
|
||||
*
|
||||
* @category TestReplyFactory
|
||||
* @package Pock\TestUtils
|
||||
*/
|
||||
class TestReplyFactory implements ReplyFactoryInterface
|
||||
{
|
||||
/** @var int */
|
||||
private $requestNumber = 0;
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function createReply(RequestInterface $request, PockResponseBuilder $responseBuilder): ResponseInterface
|
||||
{
|
||||
return $responseBuilder->withStatusCode(200)
|
||||
->withBody('Request #' . ++$this->requestNumber)
|
||||
->getResponse();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user