我正在尝试测试一个管理数据库中数据访问的类(基本上就是CRUD)。我们正在使用的DB库碰巧有一个API,您首先通过静态调用获取表对象:
function getFoo($id) {
$MyTableRepresentation = DB_DataObject::factory("mytable");
$MyTableRepresentation->get($id);
... do some stuff
return $somedata
}
......你明白了。
我们正在尝试测试这个方法,但是模拟DataObject的东西,以便(a)我们不需要测试的实际数据库连接,(b)我们甚至不需要包含DB_DataObject lib为了测试。
但是,在PHPUnit中我似乎无法获得$ this-> getMock()来正确设置静态调用。我有......
$DB_DataObject = $this->getMock('DB_DataObject', array('factory'));
...但测试仍然说未知方法“工厂”。我知道它正在创建对象,因为在它说它无法找到DB_DataObject之前。现在它可以。但是,没办法?
我真正想做的是拥有两个模拟对象,一个用于返回的表对象。因此,我不仅要指定工厂是静态调用,而且还要返回我已经设置的一些指定的其他模拟对象。
我应该提一下,我之前在SimpleTest中做了这个(无法找到代码)并且工作正常。
是什么给出了?
[UPDATE]
我开始意识到它与expect()
有关答案 0 :(得分:2)
我同意你们两个人最好不要使用静态呼叫。但是,我想我忘了提到DB_DataObject是第三方库,而静态调用是他们的代码使用的最佳实践,而不是我们的代码。还有其他方法可以使用涉及直接构造返回对象的对象。它只是将那些darned include / require语句留在使用该DB_DO类的任何类文件中。这很糟糕,因为如果你同时试图在测试中模拟一个同名的类,测试会中断(或者只是不被隔离) - 至少我认为。
答案 1 :(得分:2)
如果无法更改库,请更改对其的访问权限。将对DB_DataObject :: factory()的所有调用重构为代码中的实例方法:
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...
}
答案 2 :(得分:1)
这是代码中依赖项的一个很好的例子 - 设计使得无法注入Mock而不是真正的类。
我的第一个建议是尝试重构代码以使用实例而不是静态调用。
答案 3 :(得分:0)
DB_DataObject类缺少(或不缺少?)是一个setter,用于在调用factory方法之前传递准备好的db对象。这样,您可以在需要时传递模拟或自定义数据库对象(具有相同的接口)。
在您的测试设置中:
public function setUp() {
$mockDb = new MockDb();
DB_DataObject::setAdapter($mockDb);
}
factory()方法应该返回模拟的数据库实例。如果它尚未集成到您的类中,您可能还必须重构factory()方法以使其工作。
答案 4 :(得分:0)
在测试用例中是否需要/包含DB_DataObject的类文件?如果在PHPUnit尝试模拟对象之前该类不存在,则可能会出现这样的错误。
答案 5 :(得分:0)
使用PHPUnit MockFunction扩展和runkit,您还可以模拟静态方法。要小心,因为它是猴子修补,因此只应在极端情况下使用。不能取代良好的编程实践。