2014-06-06 40 views
8

庫: 「AWS/AWS-SDK-PHP」: 「2 *」
PHP版本:PHP 5.4.24(CLI)PHPUnit的 - 模擬S3Client都不盡如人意

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,但我們有同樣的問題,這 - $>的returnValue($預期)

。要初始化測試類和配置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()那樣配置。

一個「解決方案」(或一個糟糕的解決方案)可能會做一個S3ClientWrapper,這隻會將問題移動到其他類,不能單元測試與模擬。

有什麼想法?

UPDATE 截圖上配置MockObject與Xdebug的: enter image description here

回答

10

下面的代碼工作,並通過如預期,所以我不認爲你正在運行到引起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,如果你只是想嘲笑響應並不在乎嘲諷/磕碰的對象。

+0

With - > setMethods(['putObject'])工作正常!謝謝!! –