【问题标题】:PHPUnit print tests execution timePHPUnit 打印测试执行时间
【发布时间】:2011-03-04 17:02:49
【问题描述】:

有没有办法用 PHPUnit 打印每个测试的执行时间?

【问题讨论】:

    标签: php phpunit execution-time


    【解决方案1】:

    添加更多方法:


    您可以写一个custom Test listeneradd it to the XML file。在该侦听器中,您可以访问$testResult->time()。 phpunit.xml 中的一些行和一个 10 行的 PHP 类。没有太多麻烦。

    class SimpleTestListener implements PHPUnit_Framework_TestListener
    {
        public function endTest(PHPUnit_Framework_Test $test, $time)
        {
            printf("Test '%s' ended and took %s seconds.\n", 
               $test->getName(),
               $test->time()
            );
        }
    }
    

    如果您还是生成了一个 junit.xml(用于 CI 或在创建代码覆盖率时),所有数字都在那里,并且使用简单的 XSLT 您可以使这些数字更具可读性。

    junit.xml 示例

    <?xml version="1.0" encoding="UTF-8"?>
    <testsuites>
      <testsuite name="DemoTest" file="/home/edo/foo.php" tests="2" assertions="2" failures="1" errors="0" time="0.007727">
        <testcase name="testPass" class="DemoTest" file="/home/edo/foo.php" line="4" assertions="1" time="0.003801"/>
        <testcase name="testFail" class="DemoTest" file="/home/edo/foo.php" line="8" assertions="1" time="0.003926">
          <failure type="PHPUnit_Framework_ExpectationFailedException">DemoTest::testFail
    Failed asserting that &lt;boolean:false&gt; is true.
    
    /home/edo/foo.php:9
    </failure>
        </testcase>
      </testsuite>
    </testsuites>
    

    还有这样的转换:

    <?xml version="1.0"?>
    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="/">
      <html>
      <body>
        <h1>Tests</h1>
        <xsl:for-each select="testsuites/testsuite">
          <h2><xsl:value-of select="@name"/></h2>
          <ul>
            <xsl:for-each select="testcase">
              <li>
                <xsl:value-of select="@name"/> : <xsl:value-of select="@time"/>
                <xsl:if test="failure">
                  <b>Failed !</b>
                  <i><xsl:value-of select="*"/></i>
                </xsl:if>
              </li>
            </xsl:for-each>
          </ul>
        </xsl:for-each>
      </body>
      </html>
    </xsl:template>
    </xsl:stylesheet>
    

    你会得到显示你的行:&lt;li&gt;testPass : 0.003801&lt;/li&gt;(HTML 只是一个例子,它应该很容易适应)。

    在此处引用我自己的博文:https://edorian.github.io/2011-01-19-creating-your-custom-phpunit-output.formats/ 获取 xslt 内容。

    【讨论】:

    • 我的听众扩展了PHPUnit_Framework_BaseTestListener,我发现$test-&gt;time() 在过去的时间内不起作用。但是,根据本页其他地方的评论,$test-&gt;getTestResultObject()-&gt;time() 工作正常。 $time 也适用于每次测试时间。
    • 前两个链接失效。您能否更新它们和/或发布一些示例代码以将其添加到 XML 文件中?
    【解决方案2】:

    只需添加 --log-junit "my_tests_log.xml",然后使用电子表格应用程序(Excel、Numbers、Calc)打开此文件即可查看。您会得到您要求的所有信息,并且可以按测试执行时间排序。

    【讨论】:

      【解决方案3】:

      如果您不喜欢编写 Testlistener,就像已经建议的那样,您可以使用以下脚本以更易于阅读的格式解析 PHPUnit's JSON Test Result

      alias phpunit-report-runtime="phpunit --log-json php://stdout \
          | awk '\$NF ~ '/,/' && \$1 ~ /\"(test|time)\"/' \
          | cut -d: -f2- \
          | sed \"N;s/\n/--/\"  \
          | sed \"s/,//\"   \
          | awk 'BEGIN{FS=\"--\"}; {print \$2 \$1}' | sort -r \
          | head -n 5"
      

      格式为&lt;time in seconds&gt;, &lt;test method&gt;。示例输出:

       $ phpunit-report-runtime
       0.29307007789612, "VCR\\Util\\SoapClientTest::testDoRequestHookDisabled"
       0.16475319862366, "VCR\\CassetteTest::testRecordAndPlaybackRequest"
       0.092710018157959, "VCR\\Util\\SoapClientTest::testDoRequest"
       0.031861782073975, "VCR\\LibraryHooks\\SoapTest::testShouldInterceptCallWhenEnabled"
       0.026772022247314, "VCR\\LibraryHooks\\AbstractFilterTest::testRegisterAlreadyRegistered"
      

      【讨论】:

      • 在 PHPUnit 6 中不可能,因为 --log-json 选项已被删除。
      【解决方案4】:

      当前的许多答案都讨论了如何访问和分析日志文件中的持续时间。我将分享两种在 phpUnit 版本 3.7.38 中修改 CLI 输出的方法(这是 Travis-CI 默认用于 PHP 的),基于 @edorian 的 incomplete answer


      使用自定义打印机覆盖 CLI 输出。我找不到任何打印机文档,但它们似乎受支持。您可以在source code 中查看可用的方法。

      class TestDurationPrinter extends PHPUnit_TextUI_ResultPrinter
      {
          public function endTest(PHPUnit_Framework_Test $test, $time)
          {
              printf("Test '%s' ended and took %s seconds.\n", 
                 $test->getName(),
                 $time
              );
          }
      }
      

      然后将这些行作为属性添加到phpunit 文件中的phpunit.xml

      printerFile="path/to/TestDurationPrinter.php"
      printerClass="TestDurationPrinter"
      

      您也可以使用--printer CLI 选项,但它不能很好地与命名空间配合使用。


      您可以通过实现PHPUnit_Framework_TestListener 接口(这与打印机使用的接口相同)使用TestListener 添加到CLI 输出,而不是覆盖它。这仍然会打印 .SF,所以如果你愿意,请务必考虑到这一点。

      class TestDurationListener implements PHPUnit_Framework_TestListener
      {
          public function endTest(PHPUnit_Framework_Test $test, $time)
          {
              printf("Test '%s' ended and took %s seconds.\n", 
                 $test->getName(),
                 $time
              );
          }
      
          public function addError(PHPUnit_Framework_Test $test, Exception $e, $time)
          {
          }
      
          public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time)
          {
          }
      
          public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time)
          {
          }
      
          public function addRiskyTest(PHPUnit_Framework_Test $test, Exception $e, $time)
          {
          }
      
          public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time)
          {
          }
      
          public function startTest(PHPUnit_Framework_Test $test)
          {
          }
      
          public function startTestSuite(PHPUnit_Framework_TestSuite $suite)
          {
          }
      
          public function endTestSuite(PHPUnit_Framework_TestSuite $suite)
          {
          }
      }
      

      在 3.8 及更高版本中,有一个 PHPUnit_Framework_BaseTestListener 可以扩展,以便您只定义要覆盖的方法。

      class TestDurationListener extends PHPUnit_Framework_BaseTestListener
      {
          public function endTest(PHPUnit_Framework_Test $test, $time)
          {
              printf("Test '%s' ended.\n", $test->getName());
          }
      }
      

      要包含您的新侦听器,请将这些行添加到您的 phpunit.xml 文件中:

      <listeners>
          <listener class="TestDurationListener" file="path/to/TestDurationListener.php" />
      </listeners>
      

      【讨论】:

        【解决方案5】:

        您可以实现自己的测试运行程序,例如通过扩展 PHPUnit_TextUI_TestRunner 并使其收集和打印运行时间。

        【讨论】:

        • 我希望有一种更“自动”的方式来做到这一点,但我认为这是最好的选择
        • 在测试执行期间正在收集您要求的数据 - 您只需将其保存到带有额外选项的文件中 - 请参阅我的答案。
        【解决方案6】:

        我猜你可以使用setUptearDown 方法(分别在每个测试的开始和结束时调用)

        • setUp中记录测试前的当前时间,
        • 并计算测试花费的时间,在tearDown


        当然,您必须在每个测试类中执行此操作,或者在所有测试类都继承的超类中执行此操作。

        【讨论】:

        • 它会自动为您执行此操作。没有必要每次都重做。它存储在每个测试的 PHPUnit_Framework_TestResult 实例中。只需致电$result-&gt;time(); 即可获得所花费的时间...
        • 哦;我又在这里学到了一些东西 :-) 感谢您的评论!
        • Call to undefined method MyTest::time() :(
        • $this-&gt;getTestResultObject()-&gt;time()?
        • 如何从 tearDown() 中获取测试方法名称?
        【解决方案7】:

        好吧,您可以使用Logging 导出执行时间。它不会直接作为结果输出,但您可以编写一个漂亮的报告查看器来输出日志文件的结果(来自 JSON 或 XML)。这应该可以得到你想要的......

        【讨论】:

          【解决方案8】:

          这是一个基于 edorians 回答的想法的完整示例。在 PHPunit 4 上测试。

          创建以下 PHP 类:

          class ProfilingTestListener extends PHPUnit_Framework_BaseTestListener
          {
              public function endTest(PHPUnit_Framework_Test $test, $time)
              {
                  printf("Test '%s' ended.\tTotal time %s s.\tTest time %s s.\n",
                      str_pad($test->toString(), 50),
                      number_format($test->getTestResultObject()->time(), 3),
                      number_format($time, 3)
                  );
              }
          }
          

          将以下内容添加到您的 phpunit.xml:

          <phpunit ..>
              ...
              <listeners>
                  <listener class="ProfilingTestListener"></listener>
              </listeners>
              ...
          </phpunit>
          

          示例输出:

          PHPUnit 4.7.7 by Sebastian Bergmann and contributors.
          
          Test 'FooTest::testFoo              ' ended.    Total time 2.050 s. Test time 0.026 s. 
          .Test 'FooTest::testBar             ' ended.    Total time 2.077 s. Test time 1.000 s.
          .Test 'FooTest::testBar2            ' ended.    Total time 3.077 s. Test time 0.730 s.
          

          【讨论】:

          • 我必须添加一个文件属性,&lt;listener class="ProfilingTestListener" file="ProfilingTestListener.php"&gt;&lt;/listener&gt;
          【解决方案9】:

          在我看来,最简单的解决方案是将测试统计数据导出为 json:

          $ phpunit --log-json testExport.json

          【讨论】:

          • --log-json 在 PHPUnit 6 中被弃用和删除。
          【解决方案10】:

          我喜欢@adri 的回答。它让我开始这样做:

          alias phpunittime=" | awk '\$NF ~ '/,/' && \$1 ~ /\"(test|time)\"/' \
              | cut -d: -f2- \
              | sed \"N;s/\n/--/\"  \
              | sed \"s/,//\"   \
              | awk 'BEGIN{FS=\"--\"}; {print \$2 \$1}' | sort -r \
              | head"
          

          要使用,您需要配置 PHP 单元以输出到 JSON 日志文件。为此,请让您的 phpunit.xml 看起来像这样:

          <phpunit>
              <logging>
                  <log type="json" target="/tmp/logfile.json"/>
              </logging>
          </phpunit>
          

          然后像这样使用:

          $ cat /tmp/logfile.json | phpunittime
          

          要查看多于或少于 10 个时间,例如 2 或 19,请在末尾分别使用 -n 2-n 19

          这样做的好处是它不会假设您如何调用/运行 phpunit 本身(在我的例子中,我使用 CakePHP 并像这样运行我的命令:Console/cake test app)。此外,您可以正常运行测试并查看它们的输出...您的终端不会在测试完成之前坐在那里。

          【讨论】:

            猜你喜欢
            • 2015-03-09
            • 2018-05-05
            • 1970-01-01
            • 1970-01-01
            • 2015-06-15
            • 2016-08-18
            • 2018-10-08
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多