2010-03-16 20 views
11

Sto tentando di testare una classe di interfaccia del servizio Web utilizzando PHPUnit. Fondamentalmente, questa classe effettua chiamate a un oggetto SoapClient. Sto tentando di testare questa classe in PHPUnit utilizzando il metodo getMockFromWsdl descritto qui:Come testare un servizio Web in PHPUnit attraverso più test?

http://www.phpunit.de/manual/current/en/test-doubles.html#test-doubles.stubbing-and-mocking-web-services

Tuttavia, dal momento che voglio provare più metodi di questa stessa classe, ogni volta che l'installazione l'oggetto, devo anche imposta il simulatore WSDL SoapClient oggetto. Questo sta causando un errore fatale per essere gettato:

Fatal error: Cannot redeclare class xxxx in C:\web\php5\PEAR\PHPUnit\Framework\TestCase.php(1227) : eval()'d code on line 15 

Come posso usare lo stesso oggetto fittizio attraverso più test senza dover rigenerare fuori dal WSDL ogni volta? Questo sembra essere il problema.

-

Essendo stato chiesto di inserire un codice a guardare, ecco il metodo di impostazione nella TestCase:

protected function setUp() { 
    parent::setUp(); 

    $this->client = new Client(); 

    $this->SoapClient = $this->getMockFromWsdl(
     'service.wsdl' 
    ); 

    $this->client->setClient($this->SoapClient); 
} 

risposta

1

Per uso di base, qualcosa di simile a questo dovrebbe funzionare. PHPUnit sta facendo un po 'di magia dietro le quinte. Se metti in cache l'oggetto fittizio, questo non verrà più ripreso. Basta creare una nuova copia da questa istanza memorizzata nella cache e si dovrebbe essere a posto.

<?php 
protected function setUp() { 
    parent::setUp(); 

    static $soapStub = null; // cache the mock object here (or anywhere else) 
    if ($soapStub === null) 
     $soapStub = $this->getMockFromWsdl('service.wsdl'); 

    $this->client = new Client; 
    $this->client->setClient(clone $soapStub); // clone creates a new copy 
} 
?>

In alternativa, si può probabilmente memorizzare nella cache il nome della classe con get_class e quindi creare una nuova istanza, piuttosto che una copia. Non sono sicuro di quanto PHPUnit "magico" stia facendo per l'inizializzazione, ma ne vale la pena.

<?php 
protected function setUp() { 
    parent::setUp(); 

    static $soapStubClass = null; // cache the mock object class' name 
    if ($soapStubClass === null) 
     $soapStubClass = get_class($this->getMockFromWsdl('service.wsdl')); 

    $this->client = new Client; 
    $this->client->setClient(new $soapStubClass); 
} 
?>
+0

ho postato un certo codice in questione per la revisione. Mettersi in un class_exist non sembra che risolva il problema, dato che devi comunque recuperare l'oggetto mock in qualche modo e questa classe eseguirà sempre quella valutazione. A meno che non dovessi modificare PHPUnit stesso? Cosa avevi in ​​mente in dove collocare il metodo class_exists()? – scraton

+0

N. Stop. PHPUnit * non * necessita di modifiche. Sul serio. Perché stai usando eval? La classe che esegue eval è il problema. Il controllo 'class_exists' è solo un suggerimento per impedire la ri-dichiarazione di una classe preesistente. Questo controllo deve essere inserito all'interno della classe che esegue "eval". – pestilence669

+0

PHPUnit chiama il codice eval, non il mio codice. Penso che abbia qualcosa a che fare con l'analisi del WSDL e la creazione di oggetti mock su quella definizione. – scraton

1

Perché sei creare modello di setup() se il punto è quello di ottenere definizione di classe finta una volta per l'esecuzione di file di test insieme? Se non ricordo male è gestito prima di ogni prova definita in "questa" classe di test ... Prova setUpBeforeClass()

Da http://www.phpunit.de/manual/3.4/en/fixtures.html

Inoltre, il setUpBeforeClass() e tearDownAfterClass() metodi di modello vengono chiamati prima il primo test della classe del caso di test viene eseguito e dopo l'esecuzione dell'ultimo test della classe del caso di test, rispettivamente.

-1

PHPUnit crea una classe per la simulazione basata sul WSDL. Il nome della classe, se non fornito, è costruito dal nome file .wsdl, quindi è sempre lo stesso. Attraverso i test, quando tenta di creare nuovamente la classe, si blocca.

L'unica cosa che serve è aggiungere alla definizione Mock un nome di classe di tuo, in modo da PHPUnit non crea un nome automatica, si noti il ​​secondo argomento a $ this-> getMockFromWsdl:

protected function setUp() { 
    parent::setUp(); 

    $this->client = new Client(); 

    $this->SoapClient = $this->getMockFromWsdl(
     'service.wsdl', 'MyMockClass' 
    ); 

    $this->client->setClient($this->SoapClient); 
} 

Puoi ora crea tutti i client che vuoi, cambia solo il nome della classe per ognuno.

+1

Questo ha lo stesso problema, nel secondo test si lamenterà che "MyMockClass" viene segnalato nuovamente. –

4

Questa non è una soluzione ideale, ma nella configurazione danno la SOAP deridere un nome di classe "random", per esempio

$this->_soapClient = $this->getMockFromWsdl('some.wsdl', 'SoapClient' . md5(time().rand())); 

Questo assicura che almeno quando l'installazione è chiamato tu no ottieni questo errore

0

Aggiunta my $ .02 qui .. Ho recentemente imbattuto in questa stessa situazione e dopo un po 'di frustrazione ecco come ho avuto modo di risolverlo:

class MyTest extends PHPUnit_Framework_TestCase 
    protected static $_soapMock = null; 

    public function testDoWork_WillSucceed() 
    { 
     $this->_worker->setClient(self::getSoapClient($this)); 
     $result = $this->_worker->doWork(); 
     $this->assertEquals(true, $result['success']); 
    } 

    protected static function getSoapClient($obj) 
    { 
     if(!self::$_soapMock) { 
      self::$_soapMock = $obj->getMockFromWsdl( 
       'Test/wsdl.xml', 'SoapClient_MyWorker' 
      ); 
     } 

     return self::$_soapMock; 
    } 
} 

Ho molti dei lavoratori', ciascuno nel loro la propria classe di test e ognuno dei quali ha bisogno di accedere a un oggetto SOAP simulato. Nel secondo parametro su getMockFromWsdl ho dovuto verificare che stavo passando ad ognuno un nome univoco (ad esempio SoapClient_MyWorker) o che causasse il crash di PHPUnit.

Non sono sicuro se ottenere o meno il SOAP mock da una funzione statica e ottenere l'accesso alla funzione getMockFromWsdl passando in $this come parametro è il modo migliore per ottenere ciò, ma ci andate.

0

Un modo per passare un oggetto da un test all'altro in PHPUnits è con la dipendenza di prova, se un'istanza di un particolare oggetto è troppo gravoso/tempo:

<?php 
/** 
* Pass an object from test to test 
*/ 
class WebSericeTest extends PHPUnit_Framework_TestCase 
{ 

    protected function setUp() { 
     parent::setUp(); 
     // I don't know enough about your test cases, and do not know 
     // the implications of moving code out of your setup. 
    } 

    /** 
    * First Test which creates the SoapClient mock object. 
    */ 
    public function test1() 
    { 
     $this->client = new Client(); 

     $soapClient = $this->getMockFromWsdl(
      'service.wsdl' 
     ); 

     $this->client->setClient($this->SoapClient); 
     $this->markTestIncomplete(); 
     // To complete this test you could assert that the 
     // soap client is set in the client object. Or 
     // perform some other test of your choosing. 

     return $soapClient; 
    } 

    /** 
    * Second Test depends on web service mock object from the first test. 
    * @depends test1 
    */ 
    public function test1($soapClient) 
    { 
     // you should now have the soap client returned from the first test. 
     return $soapClient; 
    } 

    /** 
    * Third Test depends on web service mock object from the first test. 
    * @depends test1 
    */ 
    public function test3($soapClient) 
    { 
     // you should now have the soap client returned from the first test. 
     return $soapClient; 
    } 
} 
?>