0%

在Laravel中如何mock curl方法

在某些情况下,我们需要调用第三方提供的HTTP API来获取特定的信息。在Laravel中,我们可以使用curl来调用HTTP API。在开发和测试的过程中,我们不可以调用真实的API,真实的API可能是需要付费的,所以我们需要Mock API的调用。

在本文中,虽然讲的是如何Mock curl,但是也可以推广到如何Mock PHP的标准方法。下面是一个简单的例子,我将针对这个例子进行mock测试。

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);
}
}

使用第三方包

php-mock/php-mock-phpunit 是一个第三包,它对PHPUnit进行了扩展,让我们能够更加轻松地对一个方法进行Mock。

使用下面的命令安装:

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

测试代码如下:

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);
}

$this->getFunctionMock('App\Service', 'curl_exec')App\Service 是指curl_exec 所在的namespace,如果你是在 App\Controller 中调用该方法,则该处就应该是 App\Controller

重载内置函数

重载namespace下的 curl_exec ,如下代码所示。

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);
}

}
}

使用类封装

curl这样PHP inbuilt函数封装到一个新的类中,我们就可以通过Mock这个新的类进而mock inbuilt函数。

代码如下所示

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);
}

}