PHPUnit - 使用 promise 方法模拟 guzzle / s3 传输类对象


PHPUnit - Mocking a Guzzle / S3 Transfer Class Object With Promise Methods

我有一个函数,看起来像:

public function downloadProjectFolder($projectId, $taskToken){
    // Download the project directory if it isn't on the server
    if(is_dir('/path/to/folder/') === false){
        $manager = $this->instantiateS3TransferObject($projectId, $taskToken);
        $promise = $manager->promise();
        $promise->wait();
    }
    else{
        return 'Project Folder Already Exists';
    }
}

如果本地计算机上尚不存在文件夹,上述方法会将文件夹从 AWS S3 下载到我的服务器上。 实际的 S3 Transfer 对象(来自 AWS PHP SDK V3 库 - 该库本身主要从 Guzzle PHP 抽象而来)由以下函数实例化:

private function instantiateS3TransferObject($projectId, $taskToken){
    $lastDatetime = time();
    return new 'Aws'S3'Transfer($this->myS3ClientObject, 's3://mys3bucket/destination/url',
        '/path/to/local/directory/', array(
            'base_dir' => 'destination/url',
            'before' => function()use($projectId, $taskToken, &$lastDatetime){
                $currentDatetime = time();
                if(($currentDatetime - $lastDatetime) >= 30){
                    $postParams = array(
                        'project_id' => $projectId,
                        'task_token' => $taskToken
                    );
                    $this->curl->post($postParams, 'http://url/endpoint');
                    $lastDatetime = $currentDatetime;
                }
            }
        )
    );
}

上面基本上开始我的文件夹下载,并每 30 秒异步命中一次自定义端点。

在这种情况下,我将如何模拟'Aws'S3'Transfer对象,以便它在返回时包含promise()方法,而该方法又返回wait()方法?

你对time无能为力,因为它是一个本机函数,不能被嘲笑。为了可测试性,您可以稍微重构它,如下所示:

class TimeGetter
{
    public function getTime()
    {
        return time();
    }
}

然后用作

$currentDatetime = $this->timeGetter->getTime();
// instead of $currentDatetime = time();

因此,您可以稍后模拟它,并在测试功能所需的任何时间返回。

您也不能为 'Aws'S3'Transfer 创建模拟,因为您在 instantiateS3TransferObject 中显式创建了一个新实例。

对于其余代码,您需要模拟Guzzlecurl。基于问题中代码片段的非常粗略的近似值:

// First Guzzle, but order doesn't matter:
$mock = new MockHandler([
    // first expected request
    function($_self, RequestInterface $request, array $options) {
        // assert $request values meet expectations
        // and return response or exception you expect from S3
        return new Response(200, ['X-Foo' => 'Bar']); 
    },
    // second expected request, if any, is either instance of Response, Exception, or a function which returns one of them
    // third expected request, etc...
]);
$handler = HandlerStack::create($mock);
// then pass it to the class under test
// assuming it is public: 
$cut->myS3ClientObject = new Client(['handler' => $handler]);
// Now curl:
$curlMock = $this->getMockBuilder(''whatever'curl'class'you'use')
    ->disableOriginalConstructor()
    ->setMethods(['post'])
    ->getMock();
$curlMock
    ->expects($this->once()) // twice, exact,  etc ...
    ->method('post')
    ->with(
        $this->equalTo(['project_id' => 'expected_project_id', 'task_token' => 'expected_token' ]),
        $this->equalTo('http://url/endpoint')
    );
//again, assuming it is public
$cut->curl = $curlMock;
// then actually execute the method you are testing:
$cut-> downloadProjectFolder('expected_project_id', 'expected_token');

您可以在官方文档中阅读更多如何测试 Guzzle。