2010-03-16 215 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; 
    } 
} 
?>