如何对执行命令行程序的 PHP 类方法进行单元测试


How can I unit test a PHP class method that executes a command-line program?

对于我正在开发的PHP应用程序,我需要阅读当前的git修订版SHA,当然,通过使用shell_exec或反引号来执行git命令行客户端,我可以轻松获得。

显然,我已经将此调用放入了自己的方法中,以便我可以轻松地在其余单元测试中隔离和模拟它。所以我的类看起来有点像这样:

class Task_Bundle
{
    public function execute()
    {
        // Do things
        $revision = $this->git_sha();
        // Do more things
    }
    protected function git_sha()
    {
        return `git rev-parse --short HEAD`;
    }
}

当然,尽管我可以通过模拟git_sha来测试大部分类,但我正在努力了解如何测试实际的 git_sha() 方法,因为我看不到为其创建已知状态的方法。我认为单元测试中没有任何真正的价值,它也调用git rev-parse来比较结果?我想知道至少断言该命令已运行,但我看不到任何方法可以获取 PHP 执行的 shell 命令的历史记录 - 即使我指定 PHP 应该使用 BASH 而不是 SH,历史列表也是空的,我认为是因为单独的反引号执行是单独的终端会话。

我很想听听关于如何测试它的任何建议,或者是否可以不测试该方法并在将来维护应用程序时小心使用它?

虽然你已经接受了答案,但让我发布一些关于单元测试应该如何工作的东西,因为即使发布的解决方案确实有效并且确实很好,它也有一些回退。

首先,您不想实际执行命令,因为这将测试外部软件。从这一刻起,您就依赖于您无法控制的外部内容(如果我想在我的 PC 上测试您的软件会发生什么?我必须安装 git 或任何 git 模拟吗?你真正想做的是查看命令行命令是否正确并且执行良好。

正如我之前为此苦苦挣扎的那样,这是我所做的:我创建了一个执行shell_exec的类。 在我想与 shell 交互的任何地方,我都会注入该类并使用它。因此,对于测试,我可以模拟类,看看是否执行了正确的方法以及是否设置了正确的参数。

这有很多好处:

  • 我有一个与外壳交互的观点。如果将来有任何变化,或者如果我需要在执行命令之前做任何事情,我可以将行为添加到类中。PHP 函数封装得很好。
  • 它适用于每台PC,即使shell_exec命令由于任何原因不起作用。测试在每台 PC(开发机器、测试服务器、集成服务器)上执行,无需担心外部依赖关系。

当然,要验证您的软件是否作为一个整体工作,您必须进行不模拟任何东西的集成测试。但这是一个完全不同的情况。单元测试应该确保你的类(或方法)做它需要做的事情。有时这并不清楚,因为您可能决定在某个地方使用一个真正的类,因为嘲笑它会导致很多开销。但是,就我而言,如果它依赖于可能未安装或由于某种原因可能无法工作的外部库/软件,则它不再是单元测试。

至少你可以做一些事情,比如检查重新调整的字符串是否匹配/^[abcdef0-9]{7}$/

我想

如果你真的想,你可以写一个虚拟git,并将其位置附加到你的环境PATH。 让它做你需要做的任何验证,然后生成与真实事物一致的输出。 如果你愿意,你甚至可以想象它把东西转发到真正的git,但你不能保证结果。

不过,你必须小心...你想要做的足够多,这样类就可以被愚弄,认为它真的很git,但是如果你让它的行为太像真实的东西,那就等于编写你自己的半屁股版本的 Git。