如何在多个测试中模拟测试PHPUnit中的Web服务?

时间:2010-03-16 21:47:49

标签: php web-services soap phpunit

我正在尝试使用PHPUnit测试Web服务接口类。基本上,这个类调用 SoapClient 对象。我试图使用此处描述的getMockFromWsdl方法在PHPUnit中测试此类:

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

6 个答案:

答案 0 :(得分:4)

这不是一个理想的解决方案,但在您的设置中为SOAP模拟提供了一个“随机”类名,例如

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

这确保至少在调用设置时您没有收到该错误。

答案 1 :(得分: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);
}
?>

答案 2 :(得分:1)

如果要在每次执行整个测试文件时获取模拟类定义,为什么要在setUp()中创建模拟?如果我没记错的话,它会在“this”测试类中定义的每个测试之前运行...尝试setUpBeforeClass()

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

此外,在运行测试用例类的第一次测试之前以及在运行测试用例类的最后一次测试之后,将调用setUpBeforeClass()和tearDownAfterClass()模板方法。

答案 3 :(得分: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;
    }
}

我有很多'worker',每个都在自己的测试类中,每个都需要访问一个模拟的SOAP对象。在getMockFromWsdl的第二个参数中,我必须确保每个传递一个唯一的名称(例如SoapClient_MyWorker),否则会导致PHPUnit崩溃。

我不确定是否从静态函数获取SOAP模型并通过传入getMockFromWsdl作为参数来访问$this函数是实现此目的的最佳方法,但是你去。

答案 4 :(得分:0)

在PHPUnits中将对象从测试传递到测试的一种方法是使用测试依赖,如果实例化特定对象太费力/耗时:

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

答案 5 :(得分:-1)

PHPUnit基于WSDL为mock创建一个类。如果没有提供,则类名由.wsdl文件名构成,因此它始终相同。在测试中,当它再次尝试创建类时,它会崩溃。

你唯一需要的是在Mock定义中添加一个自己的类名,因此PHPUnit不会创建自动名称,请注意$ this-&gt; getMockFromWsdl的第二个参数:

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

   $this->client = new Client();

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

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

您现在可以根据需要创建任意数量的客户端,只需更改每个客户端的类名。