PHPunit方法预计被调用1次,实际上被称为0次

时间:2016-08-28 11:30:29

标签: php phpunit

我已经坚持了一段时间,我不确定为什么PHPunit看不到该函数被调用。

这是我要测试的代码:

public function handle()
{
    $path = $this->request->getPath();
    $requestMethod = $this->request->getMethod();


    if (!$path) {
        $this->redirect('home');
    } else if (!$this->isMethodPathFound($path, $requestMethod)) {
        $this->redirect('404');
    } else {
        $handler = $this->getControllerFullName($this->routes[$path]['handler']);
        if (is_callable($handler)) {
            call_user_func($handler);
        } else {
            $this->redirect('404');
        }
    }
}

/**
 * @param string $path
 * @param int $statusCode
 */
public function redirect($path, $statusCode = 303)
{
    if (defined('TESTING_ENVIRONMENT') && TESTING_ENVIRONMENT) {
        return;
    }
    header(
        'Location: ' . $this->request->getProtocol() .
        $this->request->getHost() . '/' . $path,
        true,
        $statusCode
    );
    die();
}

为标题函数设置了TESTING_ENVIRONMENT变量,因此它不会在运行PHPunit时触发(我不想创建另一个类来让重定向函数只是为了能够模拟它进行一次测试)这就是测试代码:

public function testHandlePathIsEmpty()
{
    $requestMock = $this->getMockBuilder('\services\Request')->getMock();
    $requestMock->expects($this->once())->method('getPath')->willReturn('');
    $requestMock->expects($this->once())->method('getMethod')->willReturn('GET');
    $routerMock = $this->getMockBuilder('\services\Router')
        ->setConstructorArgs([$this->routes, $requestMock])
        ->enableProxyingToOriginalMethods()
        ->getMock();
    $routerMock->expects($this->once())->method('redirect')
        ->with('asdasd')->willReturn(true);
    $routerMock->handle();
}

$ routerMock对象肯定应该调用“重定向”函数,并且它表示它不会被调用。即使我在函数内部var_dump / die时,它确实会进入它内部。

感谢您的帮助!

2 个答案:

答案 0 :(得分:2)

虽然您对显示phpunit错误的完整输出犹豫不决,但您的问题很可能不是您的方法未被调用,而是未按您定义的所有期望调用它。

您的代码

$routerMock->expects($this->once())->method('redirect')
        ->with('asdasd')->willReturn(true);

转换为以下期望:方法redirect必须使用参数'asdasd'调用一次,并返回true

从您的测试代码中我看不到asdasd方法传递了redirect。当您删除with期望时,您的测试很可能会成功。

答案 1 :(得分:0)

只是为了说清楚。如果你必须模拟你想测试的类,你的代码就会变得复杂,你应该考虑以另一种方式实现你的逻辑。

如何不模拟实际测试的类,通过传递RequestRouter模拟创建新实例(路由器模拟可能没有任何逻辑,因为您不打算使用它)然后在您的代码中执行以下操作:

public function handle()
{
    $request = $this->request;
    $path = $request->getPath();
    if (!$path) {
        $this->redirect('home');
    } else if (!$this->isMethodPathFound($path, $request->getMethod())) {
        $this->redirect('404');
    } else {
        $handler = $this->getControllerFullName($this->routes[$path]['handler']);
        if (is_callable($handler)) {
            call_user_func($handler);
        } else {
            $this->redirect('404');
        }
    }
}

在你的单元测试中,你现在可以只测试

$requestMock
    ->expects($this->never())
    ->method('getMethod');

我看到这只会涵盖第二种情况,即未执行,但第三种情况也可能发生。这总是一个问题,为什么你的代码不够干净。 您应该阅读有关KISS和SOLID的内容,以使您的代码更易于测试。这个方法太复杂了,因为你可以正确地测试它。