如何在多个测试中模拟测试PHPUnit中的Web服务?
我正在尝试使用PHPUnit测试Web服务接口类。基本上,这个类调用一个SoapClient对象。我试图用这里所描述的getMockFromWsdl
方法PHPUnit来测试这个类:如何在多个测试中模拟测试PHPUnit中的Web服务?
然而,因为我想测试从同一类中的多个方法,每次我设置的对象,我也有设置模拟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);
}
对于基本用法,像这样的工作。 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);
}
?>
为什么在setUp()中创建模拟,如果要在每次执行整个测试文件时获取一次模拟类定义?如果我没有记错它在“这个”测试类中定义的每个测试之前运行...尝试setUpBeforeClass()
从http://www.phpunit.de/manual/3.4/en/fixtures.html
此外,setUpBeforeClass()和tearDownAfterClass()模板方法之前调用运行测试用例类的第一个测试,并分别运行测试用例类的最后一个测试。
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);
}
你可以现在创建尽可能多的客户端,只需更改每个客户端的类名即可。
这有同样的问题,在第二次测试中,它会抱怨'MyMockClass'被重新声明。 – 2011-05-11 16:15:12
这不是一个理想的解决方案,但在你的设置给SOAP嘲笑“随机”类名称,例如
$this->_soapClient = $this->getMockFromWsdl('some.wsdl', 'SoapClient' . md5(time().rand()));
这确保了至少在设置被称为你不得到那个错误。
添加我的$ .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测试
一种方法是用测试的依赖,如果实例化一个特定的对象太费力/耗时:
<?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;
}
}
?>
我贴一些代码了在审查的问题。放入class_exists似乎不会解决问题,因为您仍然必须以某种方式检索模拟对象,并且此类将始终运行该eval。除非我要修改PHPUnit本身? 在放置class_exists()方法的地方,你有什么想法? – scraton 2010-03-17 00:18:02
号码停止。 PHPUnit不*需要修改。认真。你为什么使用eval?执行eval的类是问题。 'class_exists'检查仅仅是一个黑客建议,以防止重新声明一个预先存在的类。此检查需要放在执行“评估”的班级内。 – pestilence669 2010-03-17 02:21:10
PHPUnit正在调用eval代码,而不是我自己的代码。我认为它在解析WSDL并从该定义中创建模拟对象时会有所影响。 – scraton 2010-03-17 04:05:09