2014-06-06 15 views
8

Biblioteca: "AWS/AWS-sdk-php": "2. *"
PHP versione: PHP 5.4.24 (CLI)PHPUnit - Mock S3Client non funziona bene

composer.json

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

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

Abbiamo creato AwsWrapper per ottenere le azioni funzionali: uploadFile, deleteFile ...
È possibile leggere la classe, con l'iniezione delle dipendenze da sottoporre a test dell'unità.
Concentrarsi sulla funzione di costruzione e interna $ this-> s3Client-> putObject (...) sulla funzione 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; 
    } 

} 

Il file di test ha il nostro iniettore e le istanze S3Client come PHPUnit MockObject. Per prendere in giro S3Client dobbiamo disabilitare il costruttore originale con Mock Builder.

per deridere S3Client:

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

Per configurare interna chiamata putObject (caso per testare con putObject buttare S3Exception, ma abbiamo lo stesso problema con $ this-> returnValue ($ atteso)

. per init classe di test e configurare sut: codice

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

Non funziona:

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

Quando eseguiamo la nostra funzione di test, l'S3Client iniettato non lancia l'eccezione o restituisce il valore previsto, restituisce sempre NULL.

Con xdebug abbiamo visto che S3Client MockObject è configurato correttamente ma non funziona come è configurato a will().

Una "soluzione" (o una soluzione non valida) potrebbe fare un S3ClientWrapper, questo sposta il problema solo su un'altra classe che non può essere sottoposta a test dell'unità con i mock.

Qualche idea?

UPDATE Schermata su Configura MockObject con xdebug: enter image description here

risposta

10

le seguenti opere di codice e passa come previsto, quindi non credo che si esegue in eventuali limitazioni causate da PHPUnit o 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']); 
    } 
} 

È inoltre possibile utilizzare il Guzzle MockPlugin se si desidera solo per deridere la risposta e non si cura di scherno/spegnendo gli oggetti.

+0

Con -> setMethods (['putObject']) funziona correttamente! Grazie!! –

Problemi correlati