Pergunta

Existe uma maneira de cobertura de código de teste dentro visual studio se eu estou usando MSTest? Ou eu tenho que comprar NCover?

É o valor NCover Empresa o dinheiro ou são os antigos betas bom o suficiente se a Microsoft não fornece construído em ferramentas para fazer a cobertura de código?

EDIT: Descrição dos produtos VS e quais incluem a cobertura de código https://www.visualstudio.com/vs/compare/

TestDriven.NET ( http://testdriven.net/ ) pode ser usado se o seu VS versão doesn' t apoiá-lo.

Foi útil?

Solução

Sim, você pode encontrar informações de cobertura de código de dentro do Visual Studio, desde que você tenha uma versão do Visual Studio que fornece essa funcionalidade, como o Team System. Ao configurar os testes de unidade em VS.NET, um arquivo localtestrun.testrunconfig será criado e adicionado como parte da solução. Clique duas vezes nesse arquivo e encontrar a opção de opção de cobertura de código do lado esquerdo da caixa de diálogo. Selecione as montagens para o qual você deseja informações de cobertura de código coleta e, em seguida, re-executar os testes de unidade. Código informações de cobertura serão recolhidos e está disponível. Para obter as informações de cobertura de código abrir a janela de resultados de teste e clique no botão resultados de cobertura de código, o que vai abrir uma janela alternativa com os resultados.

Outras dicas

MSTest inclui a cobertura de código, pelo menos ele faz na versão do VS eu tenho. No entanto, você precisa habilitar a instrumentação no testrunconfig, que é apenas feio e um grande PITA.

A opção mais fácil é usar TestDriven.NET , que pode automatizar a cobertura, mesmo para MSTest. E uma vez que utiliza o núcleo MSTest, você ainda receber todo o bem VS, como colorização (linhas vermelho / azul para o código coberto). Veja aqui (incluindo um screencast), ou desde que uma imagem diz mais que mil palavras:

texto alternativo
(fonte: mutantdesign.co.uk )

Para futuros leitores:

Uau, isso não era divertido. Espero que isso ajude alguém lá fora em terras internet.

Por favor, note que a existência de "CodeCoverage.exe" pode depender da versão do Visual Studio que você tem. E você pode ter que instalar VS (alguns versão melhorada) no servidor de compilação.

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"

Você também pode combinar as 3 UnitTests.dlls em uma chamada

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

Eu também encontrei alguns xsl na internet. (Os 3 links abaixo são praticamente o mesmo xsl)

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

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

http: //codetuner.blogspot .com / 2011/09 / converter-mstest-code-covarage-resultados-in.html

Estou postando o xsl aqui "just in case" Die aqueles de URL no futuro. Coloque o abaixo xsl em um arquivo chamado "VSCoverageToHtml.xsl" (como mencionado acima).

<?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>

Aqui está uma pequena ferramenta de linha de comando para ajuda.

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

Veja também:

Código arquivos de cobertura de fusão usando o código no VS 2012 dinâmica da Cobertura de código

Se você não tiver a edição Visual Studio Ultimate, você também pode usar esta tarefa MSBuild para gerar o relatório de cobertura de código.

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

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top