При написании тестов часто требуется замокать ответы стороннего API, но при этом часто нужно мокнуть только те методы, которые возвращают ответ, а остальные оставить оригинальными. При этом нужно собрать инстанс сервиса вместе с параметрами конструктора.
Например в Laravel это делается с помощью Service Provider и Mockery
Допустим у нас есть класс, который работает с API платежной системы
<?php
declare(strict_types = 1);
namespace App\\Billing\\Misc\\SomeBank;
use Illuminate\\Http\\Client\\Response;
use Illuminate\\Support\\Facades\\Http;
class SomeBankPayment
{
protected string $password;
protected string $username;
protected string $gatewayUrl;
protected string $returnUrl;
public function __construct(string $username, string $password, string $gatewayUrl, string $returnUrl)
{
$this->password = $password;
$this->username = $username;
$this->gatewayUrl = $gatewayUrl;
$this->returnUrl = $returnUrl;
}
public function registerOrder(SomeBankPaymentOrder $order): Response
{
return $this->request('rest/register', \\array_merge($order->toArray(), [
'returnUrl' => $this->returnUrl . '?' . \\http_build_query(\\array_merge(
['orderNumber' => $order->getOrderNumber()],
$order->getReturnParams()
)),
]));
}
public function getOrderStatus($orderId): Response
{
return $this->request('rest/getOrderStatus', [
'orderId' => $orderId,
]);
}
private function request(string $method, array $data = []): Response
{
$url = $this->gatewayUrl . $method . '?' . \\http_build_query(
\\array_merge([
'userName' => $this->username,
'password' => $this->password,
], $data)
);
return Http::post($url);
}
}
Сначала зарегистрируем его как провайдер
<?php
declare(strict_types = 1);
namespace App\\Billing\\Providers;
use Illuminate\\Support\\ServiceProvider;
use App\\Billing\\Misc\\SomeBank\\SomeBankPayment;
class SomeBankPaymentProvider extends ServiceProvider
{
/**
* Bootstrap services.
*/
public function register(): void
{
$this->app->bind(SomeBankPayment::class, static function ($app) {
$config = $app->make('config');
return new SomeBankPayment(
$config->get('billing.drivers.somebank.username'),
$config->get('billing.drivers.somebank.password'),
$config->get('billing.drivers.somebank.gateway_url'),
$config->get('billing.drivers.somebank.return_url')
);
});
}
}
'providers' => \\array_merge(
...
\\App\\Billing\\Providers\\SomeBankPaymentProvider::class,
...
)
Создадим Manager и используем его для создания драйвера
<?php
declare(strict_types = 1);
namespace App\\Billing;
use Illuminate\\Support\\Manager;
use App\\Billing\\Drivers\\SomeBankDriver;
use App\\Billing\\Misc\\SomeBank\\SomeBankPayment;
class BillingManager extends Manager
{
public function getDefaultDriver()
{
return $this->config->get('billing.default_driver');
}
protected function createSomebankDriver(): SomeBankDriver
{
$payment = \\app()->make(SomeBankPayment::class);
return new SomeBankDriver(
$payment
);
}
}
Теперь мы можем мокать SomeBankPayment в тестах.
class SomeBankDriverTest extends BillingTestCase
{
...
protected function setUp(): void
{
$config = $this->app->make('config');
//Создаем частичный мок с параметрами конструктора
$mock = Mockery::mock(SomeBankPayment::class, [
$config->get('billing.drivers.somebank.username'),
$config->get('billing.drivers.somebank.password'),
$config->get('billing.drivers.somebank.gateway_url'),
$config->get('billing.drivers.somebank.return_url'),
])->makePartial();
//подменяем его в контейнере
$this->instance(AlfabankPayment::class, $mock);
$this->paymentMock = $mock;
$this->manager = $this->app->make(BillingManager::class);
$this->driver = $this->manager->driver('somebank');
}
public function testSuccess(): void
{
...
//Мокаем ответ от метода registerOrder и getOrderStatus
$this->paymentMock->shouldReceive('registerOrder')->andReturn(
new Response(
new \\GuzzleHttp\\Psr7\\Response(200, [], \\json_encode($this->successOrderRegisterResponse))
)
);
$this->paymentMock->shouldReceive('getOrderStatus')->andReturn(
new Response(
new \\GuzzleHttp\\Psr7\\Response(200, [], \\json_encode($this->successOrderResponse))
)
);
//Теперь тут будут мокнутые ответы API
$result = $this->driver->sessionPayment($paymentRequestedDtoFirst);
...
}
}
Profit!
P.S. Если вам нравятся мои посты, подписывайтесь на мой канал https://t.me/bearlogin_dev и приглашайте друзей 🙂