PHPUnit - 模拟S3Client不能正常工作

时间:2014-06-06 11:03:54

标签: php amazon-s3 mocking tdd phpunit

图书馆:" aws / aws-sdk-php":" 2。*"
PHP版本:PHP 5.4.24(cli)

composer.json

{
    "require": {
        "php": ">=5.3.1",
        "aws/aws-sdk-php": "2.*",
        ...
    },

    "require-dev": {
        "phpunit/phpunit": "4.1",
        "davedevelopment/phpmig": "*",
        "anahkiasen/rocketeer": "*"
    },
    ...
}

我们制作了一个AwsWrapper来获取功能性操作:uploadFile,deleteFile ...
您可以阅读课程,依赖注入进行单元测试 专注于构造函数和内部$ this-> s3Client-> putObject(...)调用uploadFile函数。

<?php

namespace app\lib\modules\files;

use Aws\Common\Aws;
use Aws\S3\Exception\S3Exception;
use Aws\S3\S3Client;
use core\lib\exceptions\WSException;
use core\lib\Injector;
use core\lib\utils\System;

class AwsWrapper
{

  /**
   * @var \core\lib\Injector
   */
  private $injector;

  /**
   * @var S3Client
   */
  private $s3Client;

  /**
   * @var string
   */
  private $bucket;

  function __construct(Injector $injector = null, S3Client $s3 = null)
  {
    if( $s3 == null )
    {
      $aws = Aws::factory(dirname(__FILE__) . '/../../../../config/aws-config.php');
      $s3 = $aws->get('s3');
    }
    if($injector == null)
    {
      $injector = new Injector();
    }
    $this->s3Client = $s3;
    $this->bucket = \core\providers\Aws::getInstance()->getBucket();
    $this->injector = $injector;
  }

  /**
   * @param $key
   * @param $filePath
   *
   * @return \Guzzle\Service\Resource\Model
   * @throws \core\lib\exceptions\WSException
   */
  public function uploadFile($key, $filePath)
  {
    /** @var System $system */
    $system = $this->injector->get('core\lib\utils\System');
    $body   = $system->fOpen($filePath, 'r');
    try {
      $result = $this->s3Client->putObject(array(
        'Bucket' => $this->bucket,
        'Key'    => $key,
        'Body'   => $body,
        'ACL'    => 'public-read',
      ));
    }
    catch (S3Exception $e)
    {
      throw new WSException($e->getMessage(), 201, $e);
    }

    return $result;
  }

} 

测试文件将我们的Injector和S3Client实例作为PhpUnit MockObject。要模拟S3Client,我们必须使用Mock Builder禁用原始构造函数。

模拟S3Client:

$this->s3Client = $this->getMockBuilder('Aws\S3\S3Client')->disableOriginalConstructor()->getMock();

配置内部putObject调用(使用putObject抛出S3Exception进行测试的情况,但是我们在$ this-&gt; returnValue($ expected)中遇到了同样的问题。

初始化测试类并配置sut:

  public function setUp()
  {
    $this->s3Client = $this->getMockBuilder('Aws\S3\S3Client')->disableOriginalConstructor()->getMock();
    $this->injector = $this->getMock('core\lib\Injector');
  }

  public function configureSut()
  {
    return new AwsWrapper($this->injector, $this->s3Client);
  }

不工作代码:

$expectedArray = array(
  'Bucket' => Aws::getInstance()->getBucket(),
  'Key'    => $key,
  'Body'   => $body,
  'ACL'    => 'public-read',
);
$this->s3Client->expects($timesPutObject)
  ->method('putObject')
  ->with($expectedArray)
  ->will($this->throwException(new S3Exception($exceptionMessage, $exceptionCode)));
$this->configureSut()->uploadFile($key, $filePath);

当我们执行测试函数时,注入的S3Client不会抛出异常或返回预期值,总是返回NULL。

使用xdebug,我们已经看到S3Client MockObject配置正确,但不会按照will()配置的方式工作。

A&#34;解决方案&#34; (或者一个糟糕的解决方案)可能正在做一个S3ClientWrapper,这只会将问题转移到其他无法通过模拟进行单元测试的类。

有什么想法吗?

UPDATE 使用xdebug配置MockObject的屏幕截图: enter image description here

1 个答案:

答案 0 :(得分:12)

以下代码按预期工作和传递,因此我认为您不会遇到由PHPUnit或AWS SDK引起的任何限制。

<?php

namespace Aws\Tests;

use Aws\S3\Exception\S3Exception;
use Aws\S3\S3Client;
use Guzzle\Service\Resource\Model;

class MyTest extends \PHPUnit_Framework_TestCase
{
    public function testMockCanReturnResult()
    {
        $model = new Model([
            'Contents' => [
                ['Key' => 'Obj1'],
                ['Key' => 'Obj2'],
                ['Key' => 'Obj3'],
            ],
        ]);

        $client = $this->getMockBuilder('Aws\S3\S3Client')
            ->disableOriginalConstructor()
            ->setMethods(['listObjects'])
            ->getMock();
        $client->expects($this->once())
            ->method('listObjects')
            ->with(['Bucket' => 'foobar'])
            ->will($this->returnValue($model));

        /** @var S3Client $client */
        $result = $client->listObjects(['Bucket' => 'foobar']);

        $this->assertEquals(
            ['Obj1', 'Obj2', 'Obj3'],
            $result->getPath('Contents/*/Key')
        );
    }

    public function testMockCanThrowException()
    {
        $client = $this->getMockBuilder('Aws\S3\S3Client')
            ->disableOriginalConstructor()
            ->setMethods(['getObject'])
            ->getMock();
        $client->expects($this->once())
            ->method('getObject')
            ->with(['Bucket' => 'foobar'])
            ->will($this->throwException(new S3Exception('VALIDATION ERROR')));

        /** @var S3Client $client */
        $this->setExpectedException('Aws\S3\Exception\S3Exception');
        $client->getObject(['Bucket' => 'foobar']);
    }
}

如果您只想模拟响应并且不关心模拟/存根对象,也可以使用Guzzle MockPlugin