この記事はJAYANTHA SIRISENAさんが書かれた記事の翻訳です。
快く翻訳記事の掲載を快諾いただいたJAYANTHA SIRISENAさんにはこの場を借りて
御礼申しげます。
オリジナルはこちらを参照ください。→こちら
また翻訳はかなり意訳になっているのと、一部古いと思われる箇所には
注釈を入れています。
何か間違いにきずかれた方はお知らせくださればうれしいです。
※注意
VisualStudio2012ではStep 1 Step 2の方法ではなく、以下の方法でテスト自動化するようです。
VSTest.Console /Enablecodecoverage /InIsolation test.dll
※オリジナル記事はVisualStudio2010を対象としています。
Jenkinsで.NetのプロジェクトをCIしてるなら、そしてNCover(NUnit用のカバレッジ取得ツール)のようなツールを使わずにカバレッジ結果を取得閲覧するのは大変です。
そんなことをしなくてもいい方法があります。
以下がその方法。
テスト結果をXmlに変える必要があり、それをxslでHTMLに変換(xslt)します。
その後このHTMLをJenkinsで出力します。
Step 1
(テスト設定)
VisualStudioのソリューションのコンテキストで「Testの設定」を追加し
ターゲットファイルのDLL群を設定します。
テストの設定はこちらを参照してください
(注)VisualStudio2012ではMStest.exeよりもVSTest.Console.exeを使用する方法が推奨のようです。
これでVisualStudioの設定でテスト結果とカバレッジの設定ができます。
この設定がJenkins管理下のバージョン管理にコミットしてあることを確認します。
つまりJenkinsのワークスペースにあることを確認します。
Step 2
(テスト自動化)
JenkinsにMStestとテスト結果生成のWindowsバッチコマンドを(ビルド後に)追加します。
このバッチが動けば、reults.trxとdata.coverageができるはずです。
del results.trx mstest /testcontainer:Example\TestProject1\bin\debug\TestProject1.dll /resultsfile:results.trx /testsettings:Example\local.testsettings
MSTestのパスは「\Microsoft Visual Studio 10.0\Common7\IDE」にあります
ここでできるカバレッジファイル(data.coverage)はバイナリ形式です。
Step 3
(xsl変換)
バイナリ形式からxml、さらにxsltでhtmlに書き換えるコンソールアプリをC#で記述します。
これをJenkinsのバッチコマンドに登録します。
これがサンプルコードです。
namespace CoverageConverter { using System; using System.Xml; using System.Xml.Xsl; using Microsoft.VisualStudio.Coverage.Analysis; using System.Data; class Program { static void Main(string[] args) { string wspath = Environment.CurrentDirectory.ToString(); CoverageInfo coverage = CoverageInfo.CreateFromFile(args[0]); DataSet data = coverage.BuildDataSet(null); data.WriteXml(wspath + @"\converted.coverage.xml"); string xml = data.GetXml(); XmlDocument xmlDocument = new XmlDocument(); xmlDocument.LoadXml(xml); XslCompiledTransform myXslTransform = new XslCompiledTransform(); XmlTextWriter writer = new XmlTextWriter(wspath + @"\converted.coverage.htm", null); myXslTransform.Load(wspath + @"\style.xslt"); myXslTransform.Transform(xmlDocument, null, writer); writer.Flush(); writer.Close(); } } }
「\Microsoft Visual Studio 10.0\Common7\IDE\PrivateAssemblies」フォルダにある
「Microsoft.VisualStudio.Coverage.Analysis.dll」をVisualStudioで参照設定しておきます。
「Microsoft.VisualStudio.Coverage.Symbols.dll」をbin出力フォルダにコピーしてください。
変換用のxsltはこれです。
style.xslt
<?xml version="1.0" encoding="utf-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="html" indent="yes"/> <xsl:template match="/" > <html> <head> <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js">/* */</script> <style type="text/css"> th { background-color:#dcdcdc; border:solid 1px #a9a9a9; text-indent:2pt; font-weight:bolder; } #data { text-align: center; } </style> <script language="JavaScript" type="text/javascript" > function CreateJavescript(){ var fileref=document.createElement('script'); fileref.setAttribute("type","text/javascript"); //fileref.setAttribute("src", "script1.js"); document.getElementsByTagName("head")[0].appendChild(fileref); } function toggleDetail(control) { var ctrlId = $(control).attr('Id'); $("tr[id='"+ctrlId +"']").toggle(); } </script> <title>Code Coverage Report</title> </head> <body onload='CreateJavescript()' > <h1>Code Coverage Report</h1> <table border="1"> <tr> <th colspan="3"></th> <th>Name</th> <th>Blocks Covered</th> <th>Blocks Not Covered</th> <th>Coverage</th> </tr> <xsl:apply-templates select="//CoverageDSPriv/Module" /> </table> </body> </html> </xsl:template> <xsl:template match="Module"> <xsl:variable name="parentId" select="generate-id(./..)" /> <xsl:variable name="currentId" select="generate-id(.)" /> <tr id="{$parentId}"> <td id="{$currentId}" colspan="3" onClick="toggleDetail(this)" onMouseOver="this.style.cursor= 'pointer' ">[+]</td> <td><xsl:value-of select="ModuleName" /></td> <td id="data"><xsl:value-of select="BlocksCovered" /></td> <td id="data"><xsl:value-of select="BlocksNotCovered" /></td> <xsl:call-template name="CoverageColumn"> <xsl:with-param name="covered" select="BlocksCovered" /> <xsl:with-param name="uncovered" select="BlocksNotCovered" /> </xsl:call-template> </tr> <xsl:apply-templates select="NamespaceTable" /> <tr id="{$currentId}-end" style="display: none;"> <td colspan="5"></td> </tr> </xsl:template> <xsl:template match="NamespaceTable"> <xsl:variable name="parentId" select="generate-id(./..)" /> <xsl:variable name="currentId" select="generate-id(.)" /> <tr id="{$parentId}" style="display: none;"> <td> - </td> <td id="{$currentId}" colspan="2" onClick="toggleDetail(this)" onMouseOver="this.style.cursor= 'pointer' ">[+]</td> <td><xsl:value-of select="NamespaceName" /></td> <td id="data"><xsl:value-of select="BlocksCovered" /></td> <td id="data"><xsl:value-of select="BlocksNotCovered" /></td> <xsl:call-template name="CoverageColumn"> <xsl:with-param name="covered" select="BlocksCovered" /> <xsl:with-param name="uncovered" select="BlocksNotCovered" /> </xsl:call-template> </tr> <xsl:apply-templates select="Class" /> <tr id="{$currentId}-end" style="display: none;"> <td colspan="5"></td> </tr> </xsl:template> <xsl:template match="Class"> <xsl:variable name="parentId" select="generate-id(./..)" /> <xsl:variable name="currentId" select="generate-id(.)" /> <tr id="{$parentId}" style="display: none;"> <td> - </td> <td> - </td> <td id="{$currentId}" onClick="toggleDetail(this)" onMouseOver="this.style.cursor='pointer' ">[+]</td> <td><xsl:value-of select="ClassName" /></td> <td id="data"><xsl:value-of select="BlocksCovered" /></td> <td id="data"><xsl:value-of select="BlocksNotCovered" /></td> <xsl:call-template name="CoverageColumn"> <xsl:with-param name="covered" select="BlocksCovered" /> <xsl:with-param name="uncovered" select="BlocksNotCovered" /> </xsl:call-template> </tr> <xsl:apply-templates select="Method" /> <tr id="{$currentId}-end" style="display: none;"> <td colspan="5"></td> </tr> </xsl:template> <xsl:template match="Method"> <xsl:variable name="parentId" select="generate-id(./..)" /> <tr id="{$parentId}" style="display: none;"> <td> -</td> <td> - </td> <td> - </td> <td><xsl:value-of select="MethodName" /></td> <td id="data"><xsl:value-of select="BlocksCovered" /></td> <td id="data"><xsl:value-of select="BlocksNotCovered" /></td> <xsl:call-template name="CoverageColumn"> <xsl:with-param name="covered" select="BlocksCovered" /> <xsl:with-param name="uncovered" select="BlocksNotCovered" /> </xsl:call-template> </tr> </xsl:template> <xsl:template name="CoverageColumn"> <xsl:param name="covered" select="0" /> <xsl:param name="uncovered" select="0" /> <td id="data"> <xsl:variable name="percent" select="($covered div ($covered + $uncovered)) * 100" /> <xsl:attribute name="style"> background-color: <xsl:choose> <xsl:when test="number($percent >= 90)">#86ed60;</xsl:when> <xsl:when test="number($percent >= 70)">#ffff99;</xsl:when> <xsl:otherwise>#FF7979;</xsl:otherwise> </xsl:choose> </xsl:attribute> <xsl:if test="$percent > 0"> <xsl:value-of select="format-number($percent, '###.##' )" />% </xsl:if> <xsl:if test="$percent = 0"> <xsl:text>0.00%</xsl:text> </xsl:if> </td> </xsl:template> </xsl:stylesheet>