模拟PHPUnit中的对象来模拟静态方法调用?
我想测试一个管理数据库中数据访问的类(你知道,CRUD,本质上)。我们正在使用的数据库库恰好有一个API,其中您首先通过静态调用获取表格对象:模拟PHPUnit中的对象来模拟静态方法调用?
function getFoo($id) {
$MyTableRepresentation = DB_DataObject::factory("mytable");
$MyTableRepresentation->get($id);
... do some stuff
return $somedata
}
...您明白了。我们试图测试这个方法,但是嘲笑DataObject的东西,这样(a)我们不需要一个实际的db连接来测试,并且(b)我们甚至不需要包含DB_DataObject lib进行测试。
但是,在PHPUnit中,我似乎无法获得$ this-> getMock()来正确设置静态调用。我有...
$DB_DataObject = $this->getMock('DB_DataObject', array('factory'));
...但测试仍然说未知的方法“工厂”。我知道它正在创建该对象,因为在它表示无法找到DB_DataObject之前。现在可以。但是,没有办法?
我真正想要做的是有两个模拟对象,一个用于返回表格对象。因此,我不仅需要指定工厂是静态调用,还要返回我已经设置的一些指定的其他模拟对象。
我应该提到一个警告,我在SimpleTest前段时间做了这个(找不到代码),它工作正常。
什么给?
[更新]
我开始掌握它是与预计()
我同意你们俩最好不要使用静态调用。但是,我想我忘了提及DB_DataObject是第三方库,并且静态调用是他们的代码使用的最佳实践,而不是我们的。还有其他方法可以使用它们的对象,这些方法包括直接构建返回的对象。它只是在那些使用那个DB_DO类的类文件中留下那些晦涩的include/require语句。这很糟糕,因为如果你同时试图在你的测试中嘲笑一个同名的班级,测试将会中断(或者不会被隔离) - 至少我认为。
这是你的代码的依赖关系的一个很好的例子 - 在设计使它不可能注入在模拟而不是真正的课堂上。
我的第一个建议是尝试重构代码以使用实例而不是静态调用。
从你的DB_DataObject类中遗漏了什么(或不是?)是一个设置器,在调用factory方法之前传递一个准备好的db对象。这样,如果需要的话,你可以传递一个模拟或自定义数据库对象(使用相同的接口)。
在测试设置:
public function setUp() {
$mockDb = new MockDb();
DB_DataObject::setAdapter($mockDb);
}
工厂()方法应该返回嘲笑的数据库实例。如果它尚未集成到您的类中,您可能还必须重构factory()方法以使其工作。
您是否需要/在您的测试用例中包含DB_DataObject的类文件?如果在PHPUnit尝试模拟对象之前该类不存在,则可以获得像这样的错误。
当您无法更改库时,请更改您的访问权限。重构为DB_DataObject ::厂()的所有调用实例方法在你的代码:
function getFoo($id) {
$MyTableRepresentation = $this->getTable("mytable");
$MyTableRepresentation->get($id);
... do some stuff
return $somedata
}
function getTable($table) {
return DB_DataObject::factory($table);
}
现在你可以使用你正在测试的类的部分模拟,并有getTable()返回一个模拟表对象。
function testMyTable() {
$dao = $this->getMock('MyTableDao', array('getMock'));
$table = $this->getMock('DB_DataObject', ...);
$dao->expects($this->any())
->method('getTable')
->with('mytable')
->will($this->returnValue($table));
$table->expects...
...test...
}
使用PHPUnit MockFunction扩展加上runkit,你也可以模拟静态方法。小心,因为它是猴子修补,因此只能在极端情况下使用。不能取代良好的编程习惯。