【PHP】PHPUnit の出力ログを見やすくHTMLに変換
PHPUnit の出力ログを見やすくHTMLに変換
PHPUnitのログが見にくいためにHTMLに変換して見やすくします。
ログを出力する
PHPUnitの説明に従いまずはXML形式のログを出力するようにします。 3. XML 設定ファイル — PHPUnit latest Manual
<logging>
<log type="junit" target="./tests/log/logfile.xml"/>
</logging>
これでPHPUnit実行後に ./tests/log/logfile.xml のログファイルが出力されます。
vendor/bin/phpunit
出力ファイル
<?xml version="1.0" encoding="UTF-8"?>
<testsuites>
<testsuite name="" tests="2" assertions="2" errors="0" failures="0" skipped="0" time="1.102644">
<testsuite name="Unit" tests="1" assertions="1" errors="0" failures="0" skipped="0" time="0.037481">
<testsuite name="Tests\Unit\ExampleTest" file="/var/www/html/tests/Unit/ExampleTest.php" tests="1" assertions="1" errors="0" failures="0" skipped="0" time="0.037481">
<testcase name="testBasicTest" class="Tests\Unit\ExampleTest" classname="Tests.Unit.ExampleTest" file="/var/www/html/tests/Unit/ExampleTest.php" line="15" assertions="1" time="0.037481" />
</testsuite>
</testsuite>
<testsuite name="Feature" tests="1" assertions="1" errors="0" failures="0" skipped="0" time="0.296235">
<testsuite name="Tests\Feature\ExampleTest" file="/var/www/html/tests/Feature/ExampleTest.php" tests="1" assertions="1" errors="0" failures="0" skipped="0" time="0.296235">
<testcase name="testBasicTest" class="Tests\Feature\ExampleTest" classname="Tests.Feature.ExampleTest" file="/var/www/html/tests/Feature/ExampleTest.php" line="15" assertions="1" time="0.296235" />
</testsuite>
</testsuite>
</testsuite>
</testsuites>
XMLからHTMLに変換する
実際に出力されたxmlファイルからHTMLファイルに変換してみます。
xsltproc を導入
xslproc を導入する必要があります。
yum -y install libxslt cf.
apt-get install xsltproc
xsltファイルの用意
XMLからHTMLファイルに変換するためにはxsltファイルの用意をしなければいけません。 僕は以下のxsltを使用させていただきました。 PHPUNIT xslt
phpunit.xslt
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!--
PHPUnit logfile XLST template
This fileset provides an easy way to view the PHPUnit XML (JUnit) logfiles in a human readable manner using a web browser.
Use this either in combination with the accompanying html file or add the following tag straight
after the xml opening tag of the logfile:
<?xml-stylesheet type="text/xsl" href="phpunit.xslt"?>
The thresholds used for the colour-coding can be changed by adjusting the variables at the top of this file.
Be careful when changing the values: the double quoting is intentional and needed. Don't remove.
Copyright ©2014 Juliette Reinders Folmer (Twitter: @jrf_nl / GitHub: @jrfnl)
License: DWTFYW
Updates will be published via: https://gist.github.com/jrfnl/3c28ea6d9b07fd48656d
Loosely inspired by: https://www.ruby-forum.com/topic/120869
NB: This was quickly thrown together. There are probably better ways to do bits of it.
Feel free to suggest them via the gist comment form ;-)
-->
<!--
The percentage of tests which need to have failed for the failures cell colour to go from orange to red.
Default value: 0.4 (=40%)
-->
<xsl:variable name="fail_bad" select="'0.4'" />
<!--
The thresholds used for the time colour coding in seconds.
-->
<xsl:variable name="very_bad_time" select="'10'" />
<xsl:variable name="bad_time" select="'3'" />
<xsl:variable name="not_so_good_time" select="'1'" />
<xsl:variable name="good_time" select="'0.4'" />
<!--
Whether to show the details of passed tests. Set to 1 to show.
Default value: 0 (no)
-->
<xsl:variable name="show_success_detail" select="'0'" />
<xsl:template match="/">
<html>
<head>
<style type="text/css">
* {
font-family: tahoma, verdana, sans-serif;
font-size: 96%;
}
a {
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
h3, h4 {
padding: 1em 0 0.5em;
}
table {
width: 100%;
border-collapse: collapse;
}
table#high-level {
width: auto;
}
table tr {
vertical-align: top;
}
table td, table th {
padding: 0.2em 1em 0.3em 1em;
}
table th {
text-align: left;
}
table tr.top {
background-color: #dddddd;
border-bottom: 2px solid #000000;
}
table tr.test-file {
margin-top: 0.2em;
font-weight: bold;
background-color: #eeeeee;
border-top: 1px solid #666666;
}
table tr.test-file th {
padding-top: 0.5em;
}
table tr.single-test th {
padding-left: 3em;
}
table#summary td {
border-left: 1px solid #cccccc;
border-right: 1px solid #cccccc;
}
table td.nr {
text-align: center;
}
table td.time {
text-align: right;
}
.pass {
color: #999999;
background-color: #C1FFC1;
}
.failed {
background-color: #FFDAB9;
}
.errored {
background-color: #FFB8BA;
}
.fail, .fail-lot {
color: #FFFFEE;
background-color: #C51F1F;
font-weight: bold;
}
.fail-some {
color: #FFFFEE;
background-color: #FFB90F;
font-weight: bold;
}
.very-bad-time {
background-color: #FA8072;
font-weight: bold;
}
.bad-time {
background-color: #FFA54F;
}
.not-so-good-time {
background-color: #FFEC8B;
}
.sort-of-ok {
background-color: #BFEFFF;
}
.good-time {
background-color: #C1FFC1;
}
.no-tests {
text-decoration: line-through;
text-decoration-style: double;
}
.error-detail-type, .fail-detail-type {
padding-left: 2em;
font-weight: bold;
}
.error-detail-detail, .fail-detail-detail {
padding-left: 4em;
white-space: pre-wrap;
padding-bottom: 1em;
}
.backlink {
text-align: right;
font-size: 80%;
}
.backlink span {
font-size: 140%;
}
</style>
</head>
<body>
<xsl:apply-templates/>
</body>
</html>
</xsl:template>
<xsl:template match="/testsuites/testsuite" mode="high-level">
<h2>Test Totals: <xsl:value-of select="@name"/></h2>
<table id="high-level">
<tr>
<td><b>Number of Tests:</b></td>
<td><xsl:value-of select="@tests"/></td>
</tr>
<tr>
<td><b>Number of Assertions:</b></td>
<td><xsl:value-of select="@assertions"/></td>
</tr>
<tr>
<td><b>Number of Failures:</b></td>
<td><xsl:value-of select="@failures"/></td>
</tr>
<tr>
<td><b>Number of Errors:</b></td>
<td><xsl:value-of select="@errors"/></td>
</tr>
<tr>
<td><b>Execution Time:</b></td>
<td><xsl:value-of select="@time"/></td>
</tr>
</table>
</xsl:template>
<xsl:template match="testsuites">
<div>
<xsl:apply-templates select="/testsuites/testsuite" mode="high-level"/>
</div>
<h3>Summary</h3>
<table id="summary">
<tr class="top">
<th>Name</th>
<th>Tests</th>
<th>Assertions</th>
<th>Failures</th>
<th>Errors</th>
<th>Execution Time</th>
</tr>
<xsl:apply-templates select="//testsuite"/>
</table>
<xsl:apply-templates select="//testsuite[count(testsuite) = 0]" mode="details"/>
</xsl:template>
<xsl:template match="//testsuite">
<xsl:variable name="hasfailures">
<xsl:choose>
<xsl:when test="(@failures div @tests ) > $fail_bad"> fail-lot</xsl:when>
<xsl:when test="@failures > 0"> fail-some</xsl:when>
<xsl:when test="@tests > 0 and @failures = 0 and @errors = 0"> pass</xsl:when>
<xsl:otherwise></xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="haserrors">
<xsl:choose>
<xsl:when test="@errors > 0"> fail</xsl:when>
<xsl:when test="@tests > 0 and @failures = 0 and @errors = 0"> pass</xsl:when>
<xsl:otherwise></xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="passes">
<xsl:choose>
<xsl:when test="@tests > 0 and @failures = 0 and @errors = 0"> pass</xsl:when>
<xsl:otherwise></xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="isslow">
<xsl:choose>
<xsl:when test="@time > $very_bad_time"> very-bad-time</xsl:when>
<xsl:when test="@time > $bad_time"> bad-time</xsl:when>
<xsl:when test="@time > $not_so_good_time"> not-so-good-time</xsl:when>
<xsl:when test="@tests > 0 and @time < $good_time"> good-time</xsl:when>
<xsl:when test="@tests = 0"></xsl:when>
<xsl:otherwise> sort-of-ok</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="testname" select="@name" />
<xsl:choose>
<xsl:when test="count(testsuite) = 0">
<tr class="single-test">
<xsl:choose>
<xsl:when test="count(testcase) > 0">
<th><a href="#{$testname}"><xsl:value-of select="@name"/></a></th>
</xsl:when>
<xsl:otherwise>
<th><xsl:value-of select="@name"/></th>
</xsl:otherwise>
</xsl:choose>
<td class="nr{$passes}"><xsl:value-of select="@tests"/></td>
<td class="nr{$passes}"><xsl:value-of select="@assertions"/></td>
<td class="nr{$hasfailures}"><xsl:value-of select="@failures"/></td>
<td class="nr{$haserrors}"><xsl:value-of select="@errors"/></td>
<td class="time{$isslow}"><xsl:value-of select="@time"/></td>
</tr>
</xsl:when>
<xsl:when test="@name != ''">
<tr class="test-file">
<th><xsl:value-of select="@name"/></th>
<td class="nr{$passes}"><xsl:value-of select="@tests"/></td>
<td class="nr{$passes}"><xsl:value-of select="@assertions"/></td>
<td class="nr{$hasfailures}"><xsl:value-of select="@failures"/></td>
<td class="nr{$haserrors}"><xsl:value-of select="@errors"/></td>
<td class="time{$isslow}"><xsl:value-of select="@time"/></td>
</tr>
</xsl:when>
<xsl:otherwise></xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="//testsuite[count(testsuite) = 0]" mode="details">
<xsl:variable name="testname" select="@name" />
<xsl:choose>
<xsl:when test="count(testcase) > 0 and ( $show_success_detail = 1 or ( $show_success_detail = 0 and ( @failures > 0 or @errors > 0 ) ) )">
<h4 id="{$testname}"><xsl:value-of select="@name"/></h4>
<table>
<tr class="top">
<th>Test name</th>
<th>Assertions</th>
<th>Time</th>
</tr>
<xsl:apply-templates select="testcase"/>
</table>
<p class="backlink"><a href="#summary"><span>↥</span> Back to summary</a></p>
</xsl:when>
<xsl:when test="count(testcase) = 0">
<h4 id="{$testname}"><span class="no-tests"><xsl:value-of select="@name"/></span> (no tests found)</h4>
</xsl:when>
<xsl:otherwise></xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="testcase">
<xsl:variable name="isslow">
<xsl:choose>
<xsl:when test="@time > $very_bad_time"> very-bad-time</xsl:when>
<xsl:when test="@time > $bad_time"> bad-time</xsl:when>
<xsl:when test="@time > $not_so_good_time"> not-so-good-time</xsl:when>
<xsl:when test="@tests > 0 and @time < $good_time"> good-time</xsl:when>
<xsl:when test="@tests = 0"></xsl:when>
<xsl:otherwise> sort-of-ok</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="class">
<xsl:choose>
<xsl:when test="count(failure) > 0">failed</xsl:when>
<xsl:when test="count(error) > 0">errored</xsl:when>
<xsl:otherwise>pass</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<tr class="{$class}">
<th><xsl:value-of select="@name"/></th>
<td><xsl:value-of select="@assertions"/></td>
<td class="time{$isslow}"><xsl:value-of select="@time"/></td>
</tr>
<xsl:apply-templates select="error"/>
<xsl:apply-templates select="failure"/>
</xsl:template>
<xsl:template match="error">
<tr>
<td class="error-detail-type" colspan="3"><xsl:value-of select="@type"/></td>
</tr>
<tr>
<td class="error-detail-detail" colspan="3"><xsl:value-of select="."/></td>
</tr>
</xsl:template>
<xsl:template match="failure">
<tr>
<td class="fail-detail-type" colspan="3"><xsl:value-of select="@type"/></td>
</tr>
<tr>
<td class="fail-detail-detail" colspan="3"><xsl:value-of select="."/></td>
</tr>
</xsl:template>
</xsl:stylesheet>
xsltproc を実行
実際に実行してみます。
xsltproc phpunit.xslt tests/log/logfile.xml > output.html
これで output.html が出力されるようにされます。

効率化を図って
PHPUnit = 試験表になるようにすれば業務効率化出来るかと思い調べました。 試験表と自動テストを別々に書いた場合、試験表と自動テストのどちらもチェックする必要があるので1つにまとめられたらかなりの効率化が可能になると思われます。