C# MSTest 代码覆盖率
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/415562/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
MSTest Code Coverage
提问by sontek
Is there a way to test code coverage within visual studio if I'm using MSTest? Or do I have to buy NCover?
如果我使用 MSTest,有没有办法在 Visual Studio 中测试代码覆盖率?还是我必须购买NCover?
Is the NCover Enterprise worth the money or are the old betas good enough if Microsoft doesn't provide built in tools to do code coverage?
如果 Microsoft 不提供内置工具来进行代码覆盖,那么 NCover Enterprise 是否物有所值,或者旧的测试版是否足够好?
EDIT: Description of VS Products and which ones include code coverage https://www.visualstudio.com/vs/compare/
编辑:VS 产品的描述以及哪些包括代码覆盖率 https://www.visualstudio.com/vs/compare/
TestDriven.NET (http://testdriven.net/) can be used if your VS version doesn't support it.
如果您的 VS 版本不支持TestDriven.NET ( http://testdriven.net/),则可以使用它。
采纳答案by sduplooy
Yes, you can find code coverage information from within Visual Studio, provided that you have a version of Visual Studio that provides that functionality, such as the Team System. When setting up the unit tests in VS.NET, a localtestrun.testrunconfig file will be created and added as part of the solution. Double-click this file and find the option Code Coverage option on the left of the dialog. Select the assemblies for which you want to collect code coverage information and then re-run the unit tests. Code coverage information will be collected and is available. To get the code coverage information open the test results window and click on the code coverage results button, which will open an alternative window with the results.
是的,您可以从 Visual Studio 中找到代码覆盖率信息,前提是您拥有提供该功能的 Visual Studio 版本,例如 Team System。在 VS.NET 中设置单元测试时,将创建 localtestrun.testrunconfig 文件并将其添加为解决方案的一部分。双击该文件并在对话框左侧找到选项代码覆盖率选项。选择要为其收集代码覆盖率信息的程序集,然后重新运行单元测试。将收集并提供代码覆盖率信息。要获取代码覆盖率信息,请打开测试结果窗口并单击代码覆盖率结果按钮,这将打开一个包含结果的替代窗口。
回答by Marc Gravell
MSTest includes code coverage, at least it does in the version of VS I have. However, you need to enable the instrumentation in the testrunconfig, which is just ugly and a major PITA.
MSTest 包括代码覆盖率,至少在我拥有的 VS 版本中是这样。但是,您需要在 testrunconfig 中启用检测,这只是丑陋的主要 PITA。
A much easier option is to use TestDriven.NET, which can automate coverage, even for MSTest. And since it uses the MSTest core, you still get all the VS goodness such as colorization (red/blue lines for covered code). See here(including a screencast), or since an image says a thousand words:
一个更简单的选择是使用TestDriven.NET,它可以自动覆盖,即使是 MSTest。而且由于它使用 MSTest 核心,您仍然可以获得所有 VS 优点,例如着色(覆盖代码的红/蓝线)。看这里(包括截屏),或者因为一张图片说了一千个字:
(source: mutantdesign.co.uk)
(来源:mutantdesign.co.uk)
回答by Abhinav Maheshwari
If you do not have the Visual Studio ultimate edition, you can also use this MSBuild task to generate the code coverage report.
如果您没有 Visual Studio 终极版,也可以使用此 MSBuild 任务生成代码覆盖率报告。
回答by granadaCoder
(Note, this answer (here/below) is for DotNet FRAMEWORK. I've created a dotnet-core answer here : How to get code coverage report in donetcore 2 application)
(注意,此答案(此处/下方)适用于 DotNet FRAMEWORK。我在这里创建了一个 dotnet-core 答案:如何在 donetcore 2 应用程序中获取代码覆盖率报告)
...............................
……………………………………………………………………………………………………………………………………………………………………………………………………
For future readers:
对于未来的读者:
Wow, this was NOT fun. I hope this helps someone out there in internet land.
哇,这不好玩。我希望这可以帮助互联网领域的人。
Please note that the existence of "CodeCoverage.exe" may depend on the version of Visual Studio you have. And you may have to install VS (some enhanced version) in the build server.
请注意,“CodeCoverage.exe”的存在可能取决于您拥有的 Visual Studio 版本。并且您可能必须在构建服务器中安装 VS(某些增强版本)。
set __msTestExe=C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\IDE\MSTest.exe
set __codeCoverageExe=C:\Program Files (x86)\Microsoft Visual Studio 14.0\Team Tools\Dynamic Code Coverage Tools\CodeCoverage.exe
rem (the below is a custom C# console application, code seen below)
set __customCodeCoverageMergerExe=CoverageCoverterConsoleApp.exe
rem below exe is from https://www.microsoft.com/en-us/download/details.aspx?id=21714
set __msXslExe=C:\MyProgFiles\MsXslCommandLine\msxsl.exe
REM the below calls will create the binary *.coverage files
"%__codeCoverageExe%" collect /output:"D:\BuildStuff\TestResults\AAA_DynamicCodeCoverage.coverage" "%__msTestExe%" /testcontainer:"D:\BuildStuff\BuildResults\My.UnitTests.One.dll" /resultsfile:"D:\BuildStuff\TestResults\My.UnitTests.One.trx"
"%__codeCoverageExe%" collect /output:"D:\BuildStuff\TestResults\BBB_DynamicCodeCoverage.coverage" "%__msTestExe%" /testcontainer:"D:\BuildStuff\BuildResults\My.UnitTests.Two.dll" /resultsfile:"D:\BuildStuff\TestResults\My.UnitTests.Two.trx"
"%__codeCoverageExe%" collect /output:"D:\BuildStuff\TestResults\CCC_DynamicCodeCoverage.coverage" "%__msTestExe%" /testcontainer:"D:\BuildStuff\BuildResults\My.UnitTests.Three.dll" /resultsfile:"D:\BuildStuff\TestResults\My.UnitTests.Three.trx"
rem below, the first argument is the new file, the 2nd through "N" args are the result-files from the three "%__codeCoverageExe%" collect above
rem this will take the three binary *.coverage files and turn them into one .xml file
"%__customCodeCoverageMergerExe%" "D:\BuildStuff\TestResults\DynamicCodeCoverage.merged.coverage.converted.xml" "D:\BuildStuff\TestResults\AAA_DynamicCodeCoverage.coverage" "D:\BuildStuff\TestResults\BBB_DynamicCodeCoverage.coverage" "D:\BuildStuff\TestResults\CCC_DynamicCodeCoverage.coverage"
"%__msXslExe%" "D:\BuildStuff\TestResults\DynamicCodeCoverage.merged.coverage.converted.xml" "D:\BuildStuff\Xsl\VSCoverageToHtml.xsl" -o "D:\BuildStuff\TestResults\CodeCoverageReport.html"
You can also combine the 3 UnitTests.dlls into one call
您还可以将 3 个 UnitTests.dll 合并到一个调用中
REM the below calls will create the binary *.coverage files
"%__codeCoverageExe%" collect /output:"D:\BuildStuff\TestResults\ZZZ_DynamicCodeCoverage.coverage" "%__msTestExe%" /testcontainer:"D:\BuildStuff\BuildResults\My.UnitTests.One.dll" /testcontainer:"D:\BuildStuff\BuildResults\My.UnitTests.Two.dll" /testcontainer:"D:\BuildStuff\BuildResults\My.UnitTests.Three.dll" /resultsfile:"D:\BuildStuff\TestResults\My.UnitTests.AllOfThem.trx"
rem below, the first argument is the new file, the 2nd through "N" args are the result-files from the three "%__codeCoverageExe%" collect above
rem this will take the one binary *.coverage files and turn them into one .xml file
"%__customCodeCoverageMergerExe%" "D:\BuildStuff\TestResults\DynamicCodeCoverage.merged.coverage.converted.xml" "D:\BuildStuff\TestResults\ZZZ_DynamicCodeCoverage.coverage"
"%__msXslExe%" "D:\BuildStuff\TestResults\DynamicCodeCoverage.merged.coverage.converted.xml" "D:\BuildStuff\Xsl\VSCoverageToHtml.xsl" -o "D:\BuildStuff\TestResults\CodeCoverageReport.html"
VSCoverageToHtml.xsl
VScoverageToHtml.xsl
I also found some xsl in the internet. (the 3 links below are pretty much the same xsl)
我还在互联网上找到了一些 xsl。(下面的 3 个链接几乎是相同的 xsl)
http://codetuner.blogspot.com/2011_09_01_archive.html
http://codetuner.blogspot.com/2011_09_01_archive.html
http://jp.axtstar.com/?page_id=258
http://jp.axtstar.com/?page_id=258
http://codetuner.blogspot.com/2011/09/convert-mstest-code-covarage-results-in.html
http://codetuner.blogspot.com/2011/09/convert-mstest-code-covarage-results-in.html
I'm posting the xsl here "just in case" those URL's die in the future. Put the below xsl in a file called "VSCoverageToHtml.xsl" (as referenced above).
我在这里发布了 xsl,“以防万一”这些 URL 将来会消失。将下面的 xsl 放在一个名为“VSCoverageToHtml.xsl”的文件中(如上所述)。
<?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"/>
<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>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"/>
</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"/>
</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"/>
</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>
Here is a small command line tool to help.
这是一个小的命令行工具来提供帮助。
https://www.microsoft.com/en-us/download/details.aspx?id=21714
https://www.microsoft.com/en-us/download/details.aspx?id=21714
using System;
using Microsoft.VisualStudio.Coverage.Analysis;
using System.Collections.Generic;
/* References
\ThirdPartyReferences\Microsoft Visual Studio 11.0\Microsoft.VisualStudio.Coverage.Analysis.dll
\ThirdPartyReferences\Microsoft Visual Studio 11.0\Microsoft.VisualStudio.Coverage.Interop.dll
*/
namespace MyCompany.VisualStudioExtensions.CodeCoverage.CoverageCoverterConsoleApp
{
class Program
{
static int Main(string[] args)
{
if (args.Length < 2)
{
Console.WriteLine("Coverage Convert - reads VStest binary code coverage data, and outputs it in XML format.");
Console.WriteLine("Usage: ConverageConvert <destinationfile> <sourcefile1> <sourcefile2> ... <sourcefileN>");
return 1;
}
string destinationFile = args[0];
//destinationFile = @"C:\TestResults\MySuperMergedCoverage.coverage.converted.to.xml";
List<string> sourceFiles = new List<string>();
//files.Add(@"C:\MyCoverage1.coverage");
//files.Add(@"C:\MyCoverage2.coverage");
//files.Add(@"C:\MyCoverage3.coverage");
/* get all the file names EXCEPT the first one */
for (int i = 1; i < args.Length; i++)
{
sourceFiles.Add(args[i]);
}
CoverageInfo mergedCoverage;
try
{
mergedCoverage = JoinCoverageFiles(sourceFiles);
}
catch (Exception e)
{
Console.WriteLine("Error opening coverage data: {0}", e.Message);
return 1;
}
CoverageDS data = mergedCoverage.BuildDataSet();
try
{
data.WriteXml(destinationFile);
}
catch (Exception e)
{
Console.WriteLine("Error writing to output file: {0}", e.Message);
return 1;
}
return 0;
}
private static CoverageInfo JoinCoverageFiles(IEnumerable<string> files)
{
if (files == null)
throw new ArgumentNullException("files");
// This will represent the joined coverage files
CoverageInfo returnItem = null;
string path;
try
{
foreach (string sourceFile in files)
{
// Create from the current file
path = System.IO.Path.GetDirectoryName(sourceFile);
CoverageInfo current = CoverageInfo.CreateFromFile(sourceFile, new string[] { path }, new string[] { path });
if (returnItem == null)
{
// First time through, assign to result
returnItem = current;
continue;
}
// Not the first time through, join the result with the current
CoverageInfo joined = null;
try
{
joined = CoverageInfo.Join(returnItem, current);
}
finally
{
// Dispose current and result
current.Dispose();
current = null;
returnItem.Dispose();
returnItem = null;
}
returnItem = joined;
}
}
catch (Exception)
{
if (returnItem != null)
{
returnItem.Dispose();
}
throw;
}
return returnItem;
}
}
}
Also see:
另见:
Code Coverage files merging using code in VS 2012 Dynamic Code Coverage