【问题标题】:PHPunit method expected to be called 1 time, actually called 0 timesPHPunit 方法预计调用 1 次,实际调用 0 次
【发布时间】:2017-01-04 13:44:38
【问题描述】:

我在这个问题上纠结了一段时间,我不确定为什么 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 对象绝对应该调用“redirect”函数,并且它说它不会被调用..即使当我在函数内部进行 var_dump/die 时,它​​确实会进入其中。

感谢您的帮助!

【问题讨论】:

    标签: php phpunit


    【解决方案1】:

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

    不模拟您实际测试的类怎么样,通过传递RequestRouter Mock 创建新实例(Router mock 可能没有任何逻辑,因为您不会使用它)然后执行您的代码中的以下内容:

    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 的内容,以使您的代码更具可测试性。这种方法太复杂了,你可以正确地测试它。

    【讨论】:

    • 我嘲笑路由器类的原因是否定重定向函数而不是测试它的功能,因为它所做的只是一个PHP全局函数,我只想确保它被调用正确的位置。关于 SOLID,这个类是一个单一的职责,它的依赖项被注入其中,可扩展并且很容易服从 Liskov 替换。问题是为什么 PHPUnit 没有看到函数正在执行并抛出异常。
    【解决方案2】:

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

    你的代码

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

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-01-03
      • 1970-01-01
      • 2020-09-15
      • 1970-01-01
      • 2011-08-24
      相关资源
      最近更新 更多