2010-03-16 124 views
11

我正在嘗試使用PHPUnit測試Web服務接口類。基本上,這個類調用一個SoapClient對象。我試圖用這裏所描述的getMockFromWsdl方法PHPUnit來測試這個類:如何在多個測試中模擬測試PHPUnit中的Web服務?

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

然而,因爲我想測試從同一類中的多個方法,每次我設置的對象,我也有設置模擬WSDL SoapClient對象。這導致拋出一個致命的錯誤:

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

我如何使用跨多個測試相同的模擬對象,而無需每次都重新生成它從WSDL?這似乎是問題所在。

-

已經被要求張貼一些代碼來看看,這裏的TestCase的設置方法:

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

    $this->client = new Client(); 

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

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

回答

1

對於基本用法,像這樣的工作。 PHPUnit在幕後做了一些魔術。如果你緩存模擬對象,它不會被重新聲明。只需從這個緩存實例創建一個新副本,你應該很好去。

<?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 
} 
?>

或者,你也許可以與get_class緩存類的名稱,然後創建一個新的實例,而不是一個副本。我不確定PHPUnit對於初始化有多少「魔力」,但它值得一試。

<?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

我貼一些代碼了在審查的問題。放入class_exists似乎不會解決問題,因爲您仍然必須以某種方式檢索模擬對象,並且此類將始終運行該eval。除非我要修改PHPUnit本身? 在放置class_exists()方法的地方,你有什麼想法? – scraton 2010-03-17 00:18:02

+0

號碼停止。 PHPUnit不*需要修改。認真。你爲什麼使用eval?執行eval的類是問題。 'class_exists'檢查僅僅是一個黑客建議,以防止重新聲明一個預先存在的類。此檢查需要放在執行「評估」的班級內。 – pestilence669 2010-03-17 02:21:10

+0

PHPUnit正在調用eval代碼,而不是我自己的代碼。我認爲它在解析WSDL並從該定義中創建模擬對象時會有所影響。 – scraton 2010-03-17 04:05:09

1

爲什麼在setUp()中創建模擬,如果要在每次執行整個測試文件時獲取一次模擬類定義?如果我沒有記錯它在「這個」測試類中定義的每個測試之前運行...嘗試setUpBeforeClass()

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

此外,setUpBeforeClass()和tearDownAfterClass()模板方法之前調用運行測試用例類的第一個測試,並分別運行測試用例類的最後一個測試。

-1

PHPUnit爲基於WSDL的模擬創建一個類。如果沒有提供,類名將由.wsdl文件名構建,因此它總是相同的。在所有測試中,當它試圖再次創建類時,它會崩潰。

你需要的僅僅是增加了模擬定義一個類名你自己的,所以PHPUnit的不創建一個自動的名字,注意第二個參數被$ this-> getMockFromWsdl:

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

    $this->client = new Client(); 

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

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

你可以現在創建儘可能多的客戶端,只需更改每個客戶端的類名即可。

+1

這有同樣的問題,在第二次測試中,它會抱怨'MyMockClass'被重新聲明。 – 2011-05-11 16:15:12

4

這不是一個理想的解決方案,但在你的設置給SOAP嘲笑「隨機」類名稱,例如

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

這確保了至少在設置被稱爲你不得到那個錯誤。

0

添加我的$ .02這裏..我最近碰到過這個同樣的情況,在這裏有些無奈後我如何能夠解決這個問題:

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

我有很多的「工人」,各自在自己的測試類,每個測試類都需要訪問一個模擬的SOAP對象。在getMockFromWsdl的第二個參數中,我必須確保傳遞每個唯一的名稱(例如SoapClient_MyWorker),否則會導致PHPUnit崩潰。

我不知道是否從靜態函數獲取SOAP模擬,並通過在$this作爲參數傳遞者能夠訪問這些getMockFromWsdl功能是實現這一目標的最佳途徑,但亞去。從測試傳遞一個對象在PHPUnits測試

0

一種方法是用測試的依賴,如果實例化一個特定的對象太費力/耗時:

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