我正在运行 CakePHP 2.8.X ,我正在尝试为模型函数编写单元测试。
让我们调用模型Item
,然后尝试测试其getStatus
方法。
但是,该模型会在find
方法中调用getStatus
。
这样的事情:
class Item extends Model
{
public function getStatus($id) {
// Calls our `$this->Item-find` method
$item = $this->find('first', [
'fields' => ['status'],
'conditions' => ['Item.id' => $id]
]);
$status = $item['status'];
$new_status = null;
// Some logic below sets `$new_status` based on `$status`
// ...
return $new_status;
}
}
设置" $new_status
"的逻辑有点复杂,这就是我想为它编写一些测试的原因。
但是,我不完全确定如何覆盖find
内的Item::getStatus
来电。
通常,当我想模拟一个模型的函数时,我使用$this->getMock
和method('find')->will($this->returnValue($val_here))
,但我不想完全模仿我的Item
我想测试它的实际getStatus
函数。
也就是说,在我的测试功能中,我将要打电话:
// This doesn't work since `$this->Item->getStatus` calls out to
// `$this->Item->find`, which my test suite doesn't know how to compute.
$returned_status = $this->Item->getStatus($id);
$this->assertEquals($expected_status, $returned_status);
那么如何在我的测试中与我的真实Item
模型进行通信,它应该覆盖其对find
方法的内部调用?
答案 0 :(得分:2)
我知道这必须是其他人面临的问题,事实证明PHPUnit有一个非常简单的方法来解决这个问题!
This tutorial基本上给了我答案。
我确实需要创建一个模拟,但是只需要传递'find'
作为我想要模拟的方法,PHPUnit有助于将所有其他方法单独保留在我的模型中,不会覆盖它们。
上述教程的相关部分是:
将一组方法名称传递给
getMock
第二个参数会产生一个模拟对象,其中包含您已识别的方法
- 是所有存根,
- 默认情况下全部返回null,
- 很容易被覆盖
而您未识别的方法
- 都是嘲笑,
- 在调用时运行方法中包含的实际代码(强调我的),
- 不允许覆盖返回值
意思是,我可以使用该模拟模型,并直接从它调用我的getStatus
方法 。该方法将运行其真实代码,当它到达find()
时,它将返回我传入$this->returnValue
的任何内容。
我使用dataProvider
传递我想要返回的find方法,以及在assertEquals
调用中测试的结果。
所以我的测试函数看起来像:
/**
* @dataProvider provideGetItemStatus
*/
public function testGetItemStatus($item, $status_to_test) {
// Only mock the `find` method, leave all other methods as is
$item_model = $this->getMock('Item', ['find']);
// Override our `find` method (should only be called once)
$item_model
->expects($this->once())
->method('find')
->will($this->returnValue($item));
// Call `getStatus` from our mocked model.
//
// The key part here is I am only mocking the `find` method,
// so when I call `$item_model->getStatus` it is actually
// going to run the real `getStatus` code. The only method
// that will return an overridden value is `find`.
//
// NOTE: the param for `getStatus` doesn't matter since I only use it in my `find` call, which I'm overriding
$result = $item_model->getStatus('dummy_id');
$this->assertEquals($status_to_test, $result);
}
public function provideGetItemStatus() {
return [
[
// $item
['Item' => ['id' = 1, 'status' => 1, /* etc. */]],
// status_to_test
1
],
// etc...
];
}
答案 1 :(得分:0)
模拟查找的一种方法可能是使用特定于测试的子类。
您可以创建一个扩展项目和覆盖查找的TestItem,以便它不会执行数据库调用。
另一种方法是封装new_status逻辑并将其独立于模型进行单元测试