使用PHPUnit 3.6我试图在下面的控制器类中测试exec()
方法。这种方法做两件事:
- 根据对象的现有属性确定要调用的方法的名称,以及
- 如果确定的控制器方法是可调用的,则执行该方法,如果不是,则该方法抛出异常
(简化的)源代码如下:
abstract class CLIController extends Controller
{
/* irrelevant class details here */
public function exec()
{
$action = ! empty($this->opts->args[0])
? $this->opts->args[0]
: $this->default_action;
if ( ! $action || ! is_callable(array($this, $action))) {
$msg = 'Invalid controller action specified';
throw new LogicException($msg);
} else {
$this->$action(); // <---- trying to get code coverage on this line!
}
}
}
所以我的问题是
我不知道如何覆盖这部分代码:
} else {
$this->$action();
}
因为我不知道如何(或者是否可能)测试在抽象类的上下文中名称未知的方法的调用再次:要调用的方法是在子类中声明的通常我只会模拟一个抽象方法,但在这种情况下我不能,因为该方法还不存在——它将由一个子类指定。
答案可能是什么
- ???这一行可能甚至不需要覆盖,因为它本质上依赖于PHP正确调用可调用类方法的能力。如果我成功地测试了
exec()
在应该抛出异常的时候抛出异常,我就知道问题行的正确运行取决于PHP的正确运行。这是否会使最初测试它的必要性失效 - 如果有某种方法模拟抽象类,并创建一个已知名称的方法添加到模拟类中,这将解决我的问题,也是我迄今为止一直尝试做但没有成功的事情
- 我知道我可以创建一个具有已知方法名的子类,但我认为创建一个具体的子类来测试抽象的父类不是一个好主意
- 可能是我需要重构。我不想做的一件事是让子类自己实现
exec()
函数
我所尝试的
- 使用PHP的一些反射功能是徒劳的——这可能是因为我自己对反射缺乏经验,而不是它无法处理这种情况
- 回顾PHPUnit手册和API文档。不幸的是,尽管PHPUnit非常棒,但我经常发现API文档有点轻
我真的很感谢任何关于如何最好地在这里进行的指导。提前谢谢。
小时>我不同意你的规定,即"创建一个具体的子类来测试一个抽象的父类不是一个好主意。"我在测试抽象类时经常这样做,通常在测试后命名具体的子类别以明确这一点。
class CLIControllerTest extends PHPUnit_Framework_TestCase
{
public function testCallsActionMethod()
{
$controller = new CLIControllerTest_WithActionMethod(...);
// set $controller->opts->args[0] to 'action'
$controller->exec();
self::assertTrue($controller->called, 'Action method was called');
}
}
class CLIControllerTest_WithActionMethod extends CLIController
{
public $called = false;
public function action() {
$this->called = true;
}
}
进行此测试的代码很简单,可以很容易地通过检查进行验证。
我很好奇,为什么用is_callable
而不是method_exists
来避免创建数组?这可能只是个人偏好,但我想知道是否存在语义差异。