6

Is there any way to mock response and request in Guzzle?

I have a class which sends some request and I want to test.

In Guzzle doc I found a way how can I mock response and request separately. But how can I combine them?

Because, If use history stack, guzzle trying to send a real request. And visa verse, when I mock response handler can't test request.

class MyClass {

     public function __construct($guzzleClient) {

        $this->client = $guzzleClient;

    }

    public function registerUser($name, $lang)
    {

           $body = ['name' => $name, 'lang' = $lang, 'state' => 'online'];

           $response = $this->sendRequest('PUT', '/users', ['body' => $body];

           return $response->getStatusCode() == 201;        
    }

   protected function sendRequest($method, $resource, array $options = [])
   {

       try {
           $response = $this->client->request($method, $resource, $options);
       } catch (BadResponseException $e) {
           $response = $e->getResponse();
       }

       $this->response = $response;

      return $response;
  }

}

Test:

class MyClassTest {

  //....
 public function testRegisterUser()

 { 

    $guzzleMock = new \GuzzleHttp\Handler\MockHandler([
        new \GuzzleHttp\Psr7\Response(201, [], 'user created response'),
    ]);

    $guzzleClient = new \GuzzleHttp\Client(['handler' => $guzzleMock]);

    $myClass = new MyClass($guzzleClient);
    /**
    * But how can I check that request contains all fields that I put in the body? Or if I add some extra header?
    */
    $this->assertTrue($myClass->registerUser('John Doe', 'en'));


 }
 //...

}
xAoc
  • 3,018
  • 3
  • 18
  • 33
  • Post some code. The description is quite confusing. What's the point of mocking requests? Are you testing custom handlers? – Alex Blex Feb 09 '17 at 16:24
  • I've updated the code @AlexBlex. With an example of a mock response, and in the doc we can how to check request. Question, how can i mix this http://docs.guzzlephp.org/en/latest/testing.html#mock-handler and this http://docs.guzzlephp.org/en/latest/testing.html#history-middleware – xAoc Feb 10 '17 at 08:08
  • `$response = $this->sendRequest('PUT', '/users', ['body' => $body];` No trailing bracket. And `'lang' = 'ru'` should be `'lang' => 'ru'` – matchish Mar 07 '19 at 10:58

2 Answers2

9

@Alex Blex was very close.

Solution:

$container = [];
$history = \GuzzleHttp\Middleware::history($container);

$guzzleMock = new \GuzzleHttp\Handler\MockHandler([
    new \GuzzleHttp\Psr7\Response(201, [], 'user created response'),
]);

$stack = \GuzzleHttp\HandlerStack::create($guzzleMock);

$stack->push($history);

$guzzleClient = new \GuzzleHttp\Client(['handler' => $stack]);
dwenaus
  • 2,781
  • 2
  • 22
  • 27
xAoc
  • 3,018
  • 3
  • 18
  • 33
1

First of all, you don't mock requests. The requests are the real ones you are going to use in production. The mock handler is actually a stack, so you can push multiple handlers there:

$container = [];
$history = \GuzzleHttp\Middleware::history($container);

$stack = \GuzzleHttp\Handler\MockHandler::createWithMiddleware([
    new \GuzzleHttp\Psr7\Response(201, [], 'user created response'),
]);

$stack->push($history);

$guzzleClient = new \GuzzleHttp\Client(['handler' => $stack]);

After you run your tests, $container will have all transactions for you to assert. In your particular test - a single transaction. You are interested in $container[0]['request'], since $container[0]['response'] will contain your canned response, so there is nothing to assert really.

Alex Blex
  • 25,038
  • 5
  • 33
  • 63
  • I received an error [ErrorException] Argument 1 passed to GuzzleHttp\Handler\MockHandler::__invoke() must implement interface Psr\Http\Message\RequestInterface, instance of Closure given, called in /vendor/guzzlehttp/guzzle/src/HandlerStack.php on line 199 and defined – xAoc Feb 10 '17 at 09:23
  • Ah, sorry, forgot the MockHandler should use the factory to create the stack. I have updated the answer. – Alex Blex Feb 10 '17 at 10:11