【发布时间】:2011-03-04 17:02:49
【问题描述】:
有没有办法用 PHPUnit 打印每个测试的执行时间?
【问题讨论】:
标签: php phpunit execution-time
有没有办法用 PHPUnit 打印每个测试的执行时间?
【问题讨论】:
标签: php phpunit execution-time
添加更多方法:
您可以写一个custom Test listener 和add 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 <boolean:false> 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>
你会得到显示你的行:<li>testPass : 0.003801</li>(HTML 只是一个例子,它应该很容易适应)。
在此处引用我自己的博文:https://edorian.github.io/2011-01-19-creating-your-custom-phpunit-output.formats/ 获取 xslt 内容。
【讨论】:
PHPUnit_Framework_BaseTestListener,我发现$test->time() 在过去的时间内不起作用。但是,根据本页其他地方的评论,$test->getTestResultObject()->time() 工作正常。 $time 也适用于每次测试时间。
只需添加 --log-junit "my_tests_log.xml",然后使用电子表格应用程序(Excel、Numbers、Calc)打开此文件即可查看。您会得到您要求的所有信息,并且可以按测试执行时间排序。
【讨论】:
如果您不喜欢编写 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"
格式为<time in seconds>, <test method>。示例输出:
$ 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"
【讨论】:
--log-json 选项已被删除。
当前的许多答案都讨论了如何访问和分析日志文件中的持续时间。我将分享两种在 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 输出,而不是覆盖它。这仍然会打印 .、S 和 F,所以如果你愿意,请务必考虑到这一点。
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>
【讨论】:
您可以实现自己的测试运行程序,例如通过扩展 PHPUnit_TextUI_TestRunner 并使其收集和打印运行时间。
【讨论】:
我猜你可以使用setUp 和tearDown 方法(分别在每个测试的开始和结束时调用):
setUp中记录测试前的当前时间,tearDown。
当然,您必须在每个测试类中执行此操作,或者在所有测试类都继承的超类中执行此操作。
【讨论】:
$result->time(); 即可获得所花费的时间...
Call to undefined method MyTest::time() :(
$this->getTestResultObject()->time()?
好吧,您可以使用Logging 导出执行时间。它不会直接作为结果输出,但您可以编写一个漂亮的报告查看器来输出日志文件的结果(来自 JSON 或 XML)。这应该可以得到你想要的......
【讨论】:
这是一个基于 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.
【讨论】:
<listener class="ProfilingTestListener" file="ProfilingTestListener.php"></listener>
在我看来,最简单的解决方案是将测试统计数据导出为 json:
$ phpunit --log-json testExport.json
【讨论】:
--log-json 在 PHPUnit 6 中被弃用和删除。
我喜欢@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)。此外,您可以正常运行测试并查看它们的输出...您的终端不会在测试完成之前坐在那里。
【讨论】: