kenjis/ci-phpunit-test

Mock External Library

azieskurniawan opened this issue · 9 comments

Hi Everyone
I want to know how the proper way to Mock External Library. In my case, i want mock external library Guzzle.

This is my controller

<?php
defined('BASEPATH') || exit('No direct script access allowed');

use GuzzleHttp\Client;

class Blabla extends MY_Controller
{

    public function guzzle(){
        $client = new \GuzzleHttp\Client();
        $response = $client->post('blabla.com/authentication',
                        array (
                                   "headers" => ['Content-Type' => 'application/json'],
                                   "json" => ["username"=>"administrator","password"=>"password"]
                                 )
                    );

        echo $respone->getStatusCode();
    }
}

The question is how to write code test to mock and inject external library to controller?
If this case can solved, i can mock other external library.
Thank You.

$client = new \GuzzleHttp\Client();

You can't inject a mock into the $client.

If you code in the controller constructor like this:

$this->client = new \GuzzleHttp\Client();

You can inject a mock into it with $this->request->setCallable().
See https://github.com/kenjis/ci-phpunit-test/blob/master/docs/HowToWriteTests.md#request-and-use-mocks

thank for your response.
I try as you suggest. I change my controller and test code

Controller

<?php
defined('BASEPATH') || exit('No direct script access allowed');

use GuzzleHttp\Client;

class Blabla extends MY_Controller
{

    public function __construct()
    {

        parent::__construct();
        $this->client = new \GuzzleHttp\Client();
    }

    public function guzzle(){
        $client = new \GuzzleHttp\Client();
        $response = $this->client->post('blabla.com/authentication',
                        array (
                                   "headers" => ['Content-Type' => 'application/json'],
                                   "json" => ["username"=>"administrator","password"=>"password"]
                                 )
                    );

       echo  json_decode($response->getBody())->api_token;
    }
}

Test Code

class Gamas_test extends TestCase
{

	public function guzzle_test()
	{
		

		$this->request->setCallable(
			function ($CI){
				$mock = new MockHandler([
				    new Response(200, [], '{ "data": { "api_token": "MOCK TOKEN"}}')
				]);

				$handlerStack = HandlerStack::create($mock);
				$client = new Client(['handler' => $handlerStack]);
				$CI->client = $client;
			}
		);
		$output = $this->request('POST', 'gamas/guzzle');

		$this->assertContains('MOCK TOKEN',$output);
	}

}

my test results:
Failed asserting that 'a475443e0ac47a5d6b047a6f187faf43ac606819d9e1f9e0591b5e6b3fee9f3b' contains "MOCK TOKEN"

but guzzle response not return mock object.
Is there something wrong with my code?

$output = $this->request('POST', 'gamas/guzzle');

blabla/guzzle ?

It seems your code flow is okay. So I don't know what's wrong.

im sorry, the code I posted is wrong. I was write code
$output = $this->request('POST', 'blabla/guzzle');
but still can't inject mock object to controller

I changed the code in test code to be like this.

Test Code

class Gamas_test extends TestCase
{

	public function guzzle_test()
	{
		

		$this->request->setCallable(
			function ($CI){
				$client = $this->getDouble('\GuzzleHttp\Client', [ 'post' => '{ "data": { "api_token": "MOCK TOKEN"}}' ]);
				$this->verifyInvokedOnce($client, 'post');
				$CI->client = $client;
			}
		);
		$output = $this->request('POST', 'blabla/guzzle');

		$this->assertContains('MOCK TOKEN',$output);
	}

}

but still can't inject mock object to controller.

public function guzzle_test() should be public function test_guzzle()?

i changed function name to
public function test_guzzle()
still can't inject mock object to controller.

If you still have this problem, feel free to reopen.