Laravel 5.6 Mock Guzzle Response

Я пытаюсь издеваться над жадным ответом от определенного API.

Код моего контроллера выглядит так (исправлено для краткости):

class SomeClass
{

    private $guzzle;

    public function __construct(\GuzzleHttp\Client $guzzle) {
        $this->guzzle = new $guzzle();
    }

    public function makeRequest(){

        $client = $this->guzzle;

        $url = 'http//somerurl';
        $options = [];

        $response = $client->request('POST', $url, $options);    

        return $response;
    }
}

А тест выглядит примерно так (опять же отредактировано)...

public function someTest(){

     $mock = $this->createMock(\GuzzleHttp\Client::class);

     $mock->method('request')->willReturn([
         'response' => 'somedata'
     ]);

     $someClass = new $SomeClass($mock);

     $response = $someClass->makeRequest();

     $body = $response->getBody();

     ...
}

В этот момент тест возвращает «Вызов функции-члена getBody при нулевом значении»;

Как можно протестировать ответ getBody на вызов guzzle?

Заранее спасибо...

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
0
6 064
3

Ответы 3

Один из подходов к тестированию с помощью Guzzle — настроить MockHandler

http://docs.guzzlephp.org/en/stable/testing.html

Таким образом, вместо того, чтобы издеваться над клиентом guzzle, вы создаете его следующим образом:

public function someTest() {

    $mock = new MockHandler([
        new Response(200, [], 'The body!'),
        // Add more responses for each response you need
    ]);

    $handler = HandlerStack::create($mock);
    $client = new Client(['handler' => $handler]);

    $someClass = new SomeClass($client);

    $response = $someClass->makeRequest();

    $body = $response->getBody();

    $this->assertSame('The body!', $body);
}

И что потом? Куда идет этот код? Как это согласуется с тем, чего я пытаюсь добиться в написанном мной коде?

James Satori-Brunet 13.03.2019 18:05

Код живет в тесте. Вы создаете ответ вместо того, чтобы издеваться над ним. Если вы хотите использовать phpunit для имитации, вам нужно вместо этого использовать $mock->method('request')->willReturn(new Response(200, [], 'hello'));.

Harry 13.03.2019 18:32

Это не работает. Точно такая же ошибка, как и раньше.

James Satori-Brunet 13.03.2019 18:36

MockHandler требует, чтобы вы «ставили в очередь» ответы, то есть вам нужно знать, в каком порядке будут выполняться внешние вызовы API. Я сделал еще один шаг вперед и обернул MockHandler другим обработчиком, способным вставить в него фиктивный ответ в последний момент, если он еще не ждет своего часа. См. https://gist.github.com/kmuenkel/d4d473beb7b2297ac2d8cd480089a738

Просто используйте эту черту в своем тесте и вызовите $this->mockGuzzleResponses(); из метода setUp() тестового класса. В этот момент все запросы, предназначенные для прохождения через Guzzle, будут доступны для утверждений с помощью свойства $guzzleRequestLog, а все ответы могу будут имитированы путем вызова $this->guzzleHandler->append(RequestInterface); в начале вашего теста.

Просто убедитесь, что все реализации Guzzle в вашем коде созданы с помощью помощника app(Client::class) и нетnew Client. В противном случае переопределение привязки не вступит в силу. Возможно, это была ваша проблема раньше.

Взгляните на мой пакет композитора https://packagist.org/packages/doppiogancio/mocked-client.

На мой взгляд, это очень простой способ смоделировать клиент Guzzle, привязав URL-адреса запросов к ответам.

$builder = new HandlerStackBuilder();

// Add a route with a response via callback
$builder->addRoute(
    'GET', '/country/IT', static function (ServerRequestInterface $request): Response {
        return new Response(200, [], '{"id":"+39","code":"IT","name":"Italy"}');
    }
);

// Add a route with a response in a text file
$builder->addRouteWithFile('GET',  '/country/IT/json', __DIR__ . '/fixtures/country.json');

// Add a route with a response in a string
$builder->addRouteWithString('GET',  '/country/IT', '{"id":"+39","code":"IT","name":"Italy"}');

// Add a route mocking directly the response
$builder->addRouteWithResponse('GET', '/admin/dashboard', new Response(401));

$client = new Client(['handler' => $builder->build()]);

Как только вы это сделаете, у вас будет полнофункциональный клиент для обычного использования.

$response = $client->request('GET', '/country/DE/json');
$body = (string) $response->getBody();
$country = json_decode($body, true);

print_r($country);

// will return
Array
(
    [id] => +49
    [code] => DE
    [name] => Germany
)

Другие вопросы по теме