首页 文章

MSTest代码覆盖范围

提问于
浏览
21

如果我正在使用MSTest,有没有办法测试visual studio中的代码覆盖率?或者我必须购买NCover?

如果微软没有提供内置工具来进行代码覆盖,那么NCover Enterprise是否物有所值,或者旧的测试版是否足够好?

编辑:VS产品的描述以及哪些包括代码覆盖https://www.visualstudio.com/vs/compare/

如果您的VS版本不支持,可以使用TestDriven.NET(http://testdriven.net/) .

4 回答

  • 6

    是的,您可以在Visual Studio中找到代码覆盖率信息,前提是您拥有提供该功能的Visual Studio版本,例如Team System . 在VS.NET中设置单元测试时,将创建localtestrun.testrunconfig文件并将其作为解决方案的一部分添加 . 双击此文件,找到对话框左侧的“代码覆盖率”选项 . 选择要为其收集代码覆盖率信息的程序集,然后重新运行单元测试 . 代码覆盖率信息将被收集并可用 . 要获取代码覆盖率信息,请打开测试结果窗口,然后单击代码覆盖率结果按钮,这将打开包含结果的备用窗口 .

  • 0

    MSTest包括代码覆盖率,至少它在VS的版本中有 . 但是,您需要在testrunconfig中启用检测,这只是丑陋而且是主要的PITA .

    一个更容易的选择是使用TestDriven.NET,它可以自动覆盖,甚至对于MSTest . 而且由于它使用了MSTest核心,你仍然可以获得所有的VS优点,例如着色(覆盖代码的红/蓝线) . 请参阅here(包括截屏视频),或者因为图像上写了千言万语:

    alt text http://www.mutantdesign.co.uk/weblog/images/DrivingMSTestandTeamCoverageusingTes.NET_F424/MSTestAndTeamCoverage_thumb1.gif

  • 13

    对于未来的读者:

    哇,这不好玩 . 我希望这可以帮助那些在互联网上的人 .

    请注意,“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"
    

    您还可以将3个UnitTests.dlls组合成一个调用

    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

    我还在互联网上找到了一些xsl . (下面的3个链接几乎是相同的xsl)

    http://codetuner.blogspot.com/2011_09_01_archive.html

    http://jp.axtstar.com/?page_id=258

    http://codetuner.blogspot.com/2011/09/convert-mstest-code-covarage-results-in.html

    我在这里发布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>
    

    这是一个小的命令行工具来帮助 .

    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;
            }
        }
    }
    

    另见:

    Code Coverage files merging using code in VS 2012 Dynamic Code Coverage

  • 14

    如果您没有Visual Studio终极版,则还可以使用此MSBuild任务生成代码覆盖率报告 .

    http://archive.msdn.microsoft.com/vscoveragetoxmltask

相关问题