【问题标题】:How is it possible to test generated php files?如何测试生成的 php 文件?
【发布时间】:2019-08-02 09:30:21
【问题描述】:

我正在开发一个重构工具,它可以生成 php 文件。这些生成的文件也可以包含变量、函数和类。

我想编写单元测试,以测试这些文件是否按预期工作,但我不知道该怎么做。

如果我需要带有 if (! @require('my_new_file.php') ) { 的不正确文件,我仍然会收到解析错误,我无法捕捉到。

我在函数内部调用require,但所需文件中的定义在我的函数外部仍然可用。我怎么能避免呢?是否可以在一个范围内要求一个文件,这样就不会污染全局命名空间?

即使我在函数内部调用eval(file_get_contents('my_new_file.php'))my_new_file.php 中定义的函数也是全局可用的。

【问题讨论】:

  • php.net/manual/en/function.php-check-syntax.php 不再存在,但也许您可以在测试脚本中执行php -l CLI 调用?
  • 文件生成是确定性的吗?然后,您可以逐个字符地比较生成的文件,而不是评估它们。如果没有,您可以尝试通过github.com/nikic/PHP-Parser 解析它们
  • 生成的(重构的)文件可能与原始文件有很大不同,但其中的函数应该给出相同的结果。这就是我想测试的,但我必须执行它们才能做到这一点
  • @misorude 听起来不错,我建议你看看stackoverflow.com/questions/11944574/…

标签: php


【解决方案1】:

为生成的文件生成 PHPunit 测试;例如。 PhpUnitGen:

composer install paulthebaud/phpunit-generator

当这些是 CLI 脚本时,请始终使用 exit(0)exit(1)

【讨论】:

    【解决方案2】:

    函数和类总是在周围的命名空间中定义,没有什么会改变,因为它是一种语言特性。您可以将命名空间注入到生成的文件中,以防止它们污染其他命名空间,从而减少名称冲突的问题。

    除此之外,您还可以通过进程将测试代码与测试代码隔离开来。一种方法是pcntl_fork(),如果有的话,或者exec() 作为平庸的替代品。您的测试框架可能会提供另一种方法(我相信 PHPUnit 有类似的东西!),以便清理或保护全局命名空间。

    【讨论】:

      【解决方案3】:

      如果我理解你想要做什么,我会使用:

      $output = exec("php script_to_test.php");
      

      然后检查输出是否是所需的。 exec 方法在单独的进程中执行您的 php 脚本,并将输出作为字符串返回。从那里您应该能够获得测试所需的结论。您还可以解析输出并搜索通知、警告、致命错误等...

      【讨论】:

        【解决方案4】:

        正如其他人所建议的那样,听起来在单独的 php 进程中运行文件与您想要的很接近。但是您应该改用 proc_open() ,这样您就可以同时检查 stdout 和 stderr (这里的其他答案只允许您检查 STDOUT,这对检测错误没有多大帮助),如果发生错误,错误很可能是在 stderr 中打印(PHP 默认将错误打印到 stderr,而不是 stdout。)。在这篇文章的最后是一个自定义版本的 shell_exec,它允许您单独监控 stdout 和 stderr,如果您的脚本需要 stdin 数据,您可以将数据写入 stdin,您可以使用它测试单个脚本,如

        $cmd=implode(" ",array(
            "php",
            escapeshellarg("path/to/my_new_file.php"),
            // if your script needs extra arguments, add them here
        )); 
        $stdin=""; // if your script needs stdin data, add it here
        $ret=my_shell_exec($cmd,$stdin,$stdout,$stderr);
        

        之后,您的脚本放入 stderr 的任何内容现在都在 $stderr 变量中,并且它打印到 stdout 的任何内容都在 $stdout 变量中,检查它是否包含您所期望的。如果没有,您的脚本可能会以某种方式失败,$stderr/$stdout 的内容可能会告诉您如何它失败了。

        function my_shell_exec(string $cmd, string $stdin=null, string &$stdout=null, string &$stderr=null):int{
            //echo "executing \"{$cmd}\"...";
            // use a tmpfile in case stdout is so large that the pipe gets full before we read it, which would result in a deadlock.
            $stdout_handle=tmpfile(); 
            $stderr_handle=tmpfile();
            $descriptorspec = array(
                0 => array("pipe", "rb"),  // stdin is *inherited* by default, so even if $stdin is empty, we should create a stdin pipe just so we can close it.
                1 => $stdout_handle,
                2 => $stderr_handle, 
            );
            $proc=proc_open($cmd,$descriptorspec,$pipes);
            if(!$proc){
                throw \RuntimeException("proc_exec failed!");
            }
            if(!is_null($stdin) && strlen($stdin)>0){
                fwrite($pipes[0],$stdin);
            }
            fclose($pipes[0]);
            $ret=proc_close($proc);
            rewind($stdout_handle);// stream_get_contents can seek but it has let me down earlier, https://bugs.php.net/bug.php?id=76268
            rewind($stderr_handle);// 
            $stdout=stream_get_contents($stdout_handle);
            fclose($stdout_handle);
            $stderr=stream_get_contents($stderr_handle);
            fclose($stderr_handle);
            //echo "done!\n";
            return $ret;
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-07-03
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多