How to Mock curl Functions in Laravel

In some situations, we need to call HTTP APIs provided by third parties to retrieve specific information. In Laravel, we can use curl to make these HTTP API calls. During development and testing, however, we cannot call the real API — it may be a paid service — so we need to mock those API calls.

Although this post focuses on how to mock curl, the same approach can be extended to mocking any built-in PHP function. Below is a simple example that we will use as the subject of our mock tests.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class CurlService
{
public function getContentFrom($url)
{
$curl = curl_init();
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_HTTPHEADER, [
'Accept: application/json',
'Content-Type: application/json'
]);
$response = curl_exec($curl);
return json_decode($response);
}
}

Using a Third-Party Package

php-mock/php-mock-phpunit is a third-party package that extends PHPUnit, making it easy to mock built-in functions.

Install it with the following command:

composer require --dev php-mock/php-mock-phpunit

The test code looks like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
public function testShouldReturnResponseWhenCallGetContentFromGivenURL()
{
$url = 'https://generator.swagger.io/api/gen/clients/android';
$response = ['status' => 200, 'data' => ['id' => 1]];
$curl = $this->getFunctionMock('App\Service', 'curl_exec');
$curl->expects($this->any())->willReturn(json_encode($response));

$service = new CurlService();
$result = $service->getContentFrom($url);

self::assertEquals($response['status'], $result->status);
self::assertEquals($response['data']['id'], $result->data->id);
}

In $this->getFunctionMock('App\Service', 'curl_exec'), App\Service refers to the namespace where curl_exec is called. If you are calling the function from App\Controller, then that value should be App\Controller instead.

Overriding Built-in Functions via Namespace

You can override curl_exec within a specific namespace, as shown in the code below.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
namespace App\Service {

function curl_exec()
{
return json_encode(['status' => 200, 'data' => ['id' => 1]]);
}
}

namespace Tests\Service {

use App\Service\CurlService;
use PHPUnit\Framework\TestCase;

class CurlServiceTest extends TestCase
{
public function testShouldReturnResponseWhenCallGetContentFromGivenURL()
{
$url = 'https://generator.swagger.io/api/gen/clients/android';
$response = ['status' => 200, 'data' => ['id' => 1]];

$service = new CurlService();
$result = $service->getContentFrom($url);

self::assertEquals($response['status'], $result->status);
self::assertEquals($response['data']['id'], $result->data->id);
}

}
}

Wrapping with a Class

By wrapping PHP built-in functions like curl inside a new class, we can mock that class and thereby mock the built-in function.

The code looks like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
class Curl
{
private $curl;

public function __construct()
{
$this->curl = curl_init();
}

public function setopt($key, $value)
{
curl_setopt($this->curl, $key, $value);
}

public function exec()
{
return curl_exec($this->curl);
}
}

class CurlService
{
private $curl;

public function __construct($curl)
{
$this->curl = $curl;
}

public function getContentFrom($url)
{
$this->curl->setopt(CURLOPT_RETURNTRANSFER, 1);
$this->curl->setopt(CURLOPT_URL, $url);
$this->curl->setopt(CURLOPT_HTTPHEADER, [
'Accept: application/json',
'Content-Type: application/json'
]);
$response = $this->curl->exec();
return json_decode($response);
}
}

class CurlServiceTest extends TestCase
{
public function testShouldReturnWhenCallGetContentFromGivenURL()
{
$url = 'https://generator.swagger.io/api/gen/clients/android';
$response = ['status' => 200, 'data' => ['id' => 1]];
$curlMock = \Mockery::mock(Curl::class);
$curlMock->shouldReceive('setopt');
$curlMock->shouldReceive('exec')->andReturn(json_encode($response));

$service = new CurlService($curlMock);
$result = $service->getContentFrom($url);

self::assertEquals($response['status'], $result->status);
self::assertEquals($response['data']['id'], $result->data->id);
}

}