PHPFixing
  • Privacy Policy
  • TOS
  • Ask Question
  • Contact Us
  • Home
  • PHP
  • Programming
  • SQL Injection
  • Web3.0

Wednesday, March 16, 2022

[FIXED] CakePHP 3 - Mock HTTP\Client() in integration test

 March 16, 2022     cakephp, cakephp-3.0, request, unit-testing     No comments   

Issue

I am using Cake\Http\Client (book) to access some external applications. In my unit tests I cannot actually use those services.

So how to mock the Http Client in a controller integration test? It would be nice if I could for instance prescribe the exact response, without it ever making the actual HTTP request.

In my case the requests are made in a controller action and the destination is some other application on the same server. However, I feel like a solution to this should independent of the HTTP target.


Solution

I'm pretty certain there is no built-in solution to mocking the Http\Client object, considering I can't find anything in the docs or api pages.

So instead I used another PHP HTTP client, Guzzle. Guzzle does allow mocking by queuing pre-defined responses, exactly what I wanted.

However, smooth integration with CakePHP was not trivial since the Guzzle client constructor requires the mock stack to be passed. I solved this by creating a client factory:

<?php

namespace App\Guzzle;

use GuzzleHttp\Client;

/**
 * Guzzle factory
 * 
 * Created to make guzzle clients mockable
 */
class GuzzleFactory
{
    static $options = [];

    /**
     * Create new client
     * 
     * @return \GuzzleHttp\Client
     */
    static function create()
    {
        return new Client(self::$options);
    }

    /**
     * Set handler stack in global options for new clients
     * 
     * @param \GuzzleHttp\HandlerStack $handlerStack
     */
    static function setTest($handlerStack)
    {
        self::$options['handler'] = $handlerStack;
    }

    /**
     * Set options array to default
     */
    static function reset()
    {
        self::$options = [];
    }
}

Then in my controller tests I have the function:

/**
 * Set Guzzle mock responses
 * 
 * @param array $list
 */
public function setGuzzleResponses($list)
{
    if (!is_array($list))
    {
        $list = [$list];
    }
    $mock = new MockHandler($list);
    $handlerStack = HandlerStack::create($mock);
    GuzzleFactory::setTest($handlerStack);
}

Such that inside a test I can call:

    // ...
    $this->setGuzzleResponses([
        new Response(200, [], json_encode($response))
    ]);
    $this->get("/page/index"); // This request will make an HTTP call
    // ...

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



Answered By - Roberto
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg
Newer Post Older Post Home
View mobile version

0 Comments:

Post a Comment

Note: Only a member of this blog may post a comment.

Total Pageviews

Featured Post

Why Learn PHP Programming

Why Learn PHP Programming A widely-used open source scripting language PHP is one of the most popular programming languages in the world. It...

Subscribe To

Posts
Atom
Posts
Comments
Atom
Comments

Copyright © PHPFixing