Delen via


Codedekking gebruiken voor eenheidstests

Belangrijk

In dit artikel wordt het maken van het voorbeeldproject uitgelegd. Als u al een project hebt, kunt u naar de sectie Codingedekkingstools doorgaan.

Eenheidstests helpen de functionaliteit te waarborgen en een verificatiemiddel te bieden voor het herstructureren van inspanningen. Codedekking is een meting van de hoeveelheid code die wordt uitgevoerd door unit tests, namelijk regels, vertakkingen of methoden. Als u bijvoorbeeld een eenvoudige applicatie hebt met slechts twee voorwaardelijke vertakkingen van code (vertakking a en vertakking b), rapporteert een eenheidstest die de voorwaardelijke vertakking a controleert de dekking van de vertakkingscode als 50%.

In dit artikel wordt het gebruik van codedekking besproken voor eenheidstests met Coverlet en het genereren van rapporten met behulp van ReportGenerator. Hoewel dit artikel gericht is op C# en xUnit als testframework, zouden zowel MSTest als NUnit ook werken. Coverlet is een opensource-project op GitHub dat een platformoverschrijdend codedekkingsframework biedt voor C#. Coverlet maakt deel uit van de .NET Foundation. Coverlet verzamelt testuitvoeringsgegevens voor Cobertura-dekking, die wordt gebruikt voor het genereren van rapporten.

Daarnaast wordt in dit artikel beschreven hoe u de codedekkingsgegevens gebruikt die zijn verzameld uit een Coverlet-testuitvoering om een rapport te genereren. Het genereren van rapporten is mogelijk met behulp van een ander opensource-project op GitHub - ReportGenerator. ReportGenerator converteert dekkingsrapporten die door Cobertura worden gegenereerd, onder andere naar door mensen leesbare rapporten in verschillende indelingen.

Dit artikel is gebaseerd op het voorbeeldbroncodeproject dat beschikbaar is in de voorbeeldbrowser.

Systeem onder test

Het 'systeem onder test' verwijst naar de code waarmee u eenheidstests schrijft. Dit kan een object, service of iets anders zijn dat testbare functionaliteit beschikbaar maakt. Voor dit artikel maakt u een klassebibliotheek die het systeem dat getest wordt is, en twee bijbehorende eenheidstestprojecten.

Een klassebibliotheek maken

Maak vanaf een opdrachtprompt in een nieuwe map met de naam UnitTestingCodeCoverageeen nieuwe .NET Standard-klassebibliotheek met behulp van de dotnet new classlib opdracht:

dotnet new classlib -n Numbers

In het onderstaande fragment wordt een eenvoudige PrimeService klasse gedefinieerd die functionaliteit biedt om te controleren of een getal priem is. Kopieer het onderstaande fragment en vervang de inhoud van het bestand Class1.cs dat automatisch is gemaakt in de map Numbers. Wijzig de naam van het Class1.cs bestand in PrimeService.cs.

namespace System.Numbers
{
    public class PrimeService
    {
        public bool IsPrime(int candidate)
        {
            if (candidate < 2)
            {
                return false;
            }

            for (int divisor = 2; divisor <= Math.Sqrt(candidate); ++divisor)
            {
                if (candidate % divisor == 0)
                {
                    return false;
                }
            }
            return true;
        }
    }
}

Aanbeveling

Het is de moeite waard om te vermelden dat de Numbers klassebibliotheek opzettelijk is toegevoegd aan de System naamruimte. Dit maakt het System.Math mogelijk om toegankelijk te zijn zonder een using System; naamruimtedeclaratie. Zie naamruimte (C#-verwijzing) voor meer informatie.

Testprojecten maken

Maak twee nieuwe xUnit Test Project-sjablonen (.NET Core) vanaf dezelfde opdrachtprompt met behulp van de dotnet new xunit opdracht:

dotnet new xunit -n XUnit.Coverlet.Collector
dotnet new xunit -n XUnit.Coverlet.MSBuild

Beide nieuwe xUnit-testprojecten moeten een projectreferentie van de klassebibliotheek Numbers toevoegen. Dit is zo dat de testprojecten toegang hebben tot de PrimeService voor testen. Gebruik de dotnet add opdracht vanaf de opdrachtprompt:

dotnet add XUnit.Coverlet.Collector\XUnit.Coverlet.Collector.csproj reference Numbers\Numbers.csproj
dotnet add XUnit.Coverlet.MSBuild\XUnit.Coverlet.MSBuild.csproj reference Numbers\Numbers.csproj

Het MSBuild-project heeft de juiste naam, omdat het afhankelijk is van het nuget-pakket coverlet.msbuild . Voeg deze pakketafhankelijkheid toe door de opdracht uit te dotnet package add voeren. (Als u een SDK-versie van .NET 9 of eerder gebruikt, gebruikt u in plaats daarvan het dotnet add package formulier.)

cd XUnit.Coverlet.MSBuild && dotnet package add coverlet.msbuild && cd ..

Met de vorige opdracht werd de map effectief veranderd naar het MSBuild-testproject, waarna het NuGet-pakket werd toegevoegd. Toen dat werd gedaan, veranderde het vervolgens mappen, waardoor één niveau wordt opgevoerd.

Open beide UnitTest1.cs bestanden en vervang de inhoud door het volgende codefragment. Wijzig de naam van de UnitTest1.cs-bestanden in PrimeServiceTests.cs.

using System.Numbers;
using Xunit;

namespace XUnit.Coverlet
{
    public class PrimeServiceTests
    {
        readonly PrimeService _primeService;

        public PrimeServiceTests() => _primeService = new PrimeService();

        [Theory]
        [InlineData(-1), InlineData(0), InlineData(1)]
        public void IsPrime_ValuesLessThan2_ReturnFalse(int value) =>
            Assert.False(_primeService.IsPrime(value), $"{value} should not be prime");

        [Theory]
        [InlineData(2), InlineData(3), InlineData(5), InlineData(7)]
        public void IsPrime_PrimesLessThan10_ReturnTrue(int value) =>
            Assert.True(_primeService.IsPrime(value), $"{value} should be prime");

        [Theory]
        [InlineData(4), InlineData(6), InlineData(8), InlineData(9)]
        public void IsPrime_NonPrimesLessThan10_ReturnFalse(int value) =>
            Assert.False(_primeService.IsPrime(value), $"{value} should not be prime");
    }
}

Een oplossing maken

Maak vanaf de opdrachtprompt een nieuwe oplossing om de klassebibliotheek en de twee testprojecten in te kapselen. Gebruik de dotnet sln opdracht:

dotnet new sln -n XUnit.Coverage

Hiermee maakt u een nieuw oplossingsbestand met de naam XUnit.Coverage in de map UnitTestingCodeCoverage. Voeg de projecten toe aan de root van de oplossing.

dotnet sln XUnit.Coverage.sln add **/*.csproj --in-root

Bouw de oplossing met behulp van de dotnet build opdracht:

dotnet build

Als de build is geslaagd, hebt u de drie projecten gemaakt, de projecten en pakketten correct gerefereerd en de broncode correct bijgewerkt. Goed gedaan!

Hulpprogramma's voor codedekking

Er zijn twee typen hulpprogramma's voor codedekking:

  • DataCollectors: DataCollectors controleren de testuitvoering en verzamelen informatie over testuitvoeringen. Ze rapporteren de verzamelde gegevens in verschillende uitvoerindelingen, zoals XML en JSON. Zie uw eerste DataCollector voor meer informatie.
  • Rapportgeneratoren: gebruik gegevens die zijn verzameld uit testuitvoeringen om rapporten te genereren, vaak als html-stijl.

In deze sectie ligt de focus op hulpprogramma's voor gegevensverzamelaars.

.NET bevat een ingebouwde gegevensverzamelaar voor codedekking, die ook beschikbaar is in Visual Studio. Deze gegevensverzamelaar genereert een binair .dekkingsbestand dat kan worden gebruikt voor het genereren van rapporten in Visual Studio. Het binaire bestand kan niet door mensen worden gelezen en moet worden geconverteerd naar een door mensen leesbare indeling voordat het kan worden gebruikt om rapporten buiten Visual Studio te genereren.

Aanbeveling

Het dotnet-coverage hulpprogramma is een platformoverschrijdend hulpprogramma dat kan worden gebruikt om het bestand met binaire dekkingstests te converteren naar een door mensen leesbare indeling. Zie dotnet-dekking voor meer informatie.

Coverlet is een opensource-alternatief voor de ingebouwde collector. Het genereert testresultaten als door mensen leesbare Cobertura XML-bestanden, die vervolgens kunnen worden gebruikt om HTML-rapporten te genereren. Als u Coverlet wilt gebruiken voor codedekking, moet een bestaand eenheidstestproject over de juiste pakketafhankelijkheden beschikken of u kunt ook vertrouwen op globale .NET-hulpprogramma's en het bijbehorende nuGet-pakket coverlet.console .

Integreren met .NET-test

De xUnit-testprojectsjabloon kan standaard al worden geïntegreerd met coverlet.collector . Wijzig vanaf de opdrachtprompt de mappen in het project XUnit.Coverlet.Collector en voer de dotnet test opdracht uit:

cd XUnit.Coverlet.Collector && dotnet test --collect:"XPlat Code Coverage"

Notitie

Het "XPlat Code Coverage" argument is een makkelijk herkenbare naam die overeenkomt met de gegevensverzamelaars van Coverlet. Deze naam is vereist, maar is niet hoofdlettergevoelig. Gebruik de ingebouwde codedekkinggegevensverzamelaar van .NET met "Code Coverage".

Als onderdeel van de dotnet test uitvoering wordt een resulterend coverage.cobertura.xml bestand uitgevoerd naar de map TestResults . Het XML-bestand bevat de resultaten. Dit is een platformoverschrijdende optie die afhankelijk is van de .NET CLI en het is ideaal voor buildsystemen waarbij MSBuild niet beschikbaar is.

Hieronder ziet u het voorbeeldbestand coverage.cobertura.xml .

<?xml version="1.0" encoding="utf-8"?>
<coverage line-rate="1" branch-rate="1" version="1.9" timestamp="1592248008"
          lines-covered="12" lines-valid="12" branches-covered="6" branches-valid="6">
  <sources>
    <source>C:\</source>
  </sources>
  <packages>
    <package name="Numbers" line-rate="1" branch-rate="1" complexity="6">
      <classes>
        <class name="Numbers.PrimeService" line-rate="1" branch-rate="1" complexity="6"
               filename="Numbers\PrimeService.cs">
          <methods>
            <method name="IsPrime" signature="(System.Int32)" line-rate="1"
                    branch-rate="1" complexity="6">
              <lines>
                <line number="8" hits="11" branch="False" />
                <line number="9" hits="11" branch="True" condition-coverage="100% (2/2)">
                  <conditions>
                    <condition number="7" type="jump" coverage="100%" />
                  </conditions>
                </line>
                <line number="10" hits="3" branch="False" />
                <line number="11" hits="3" branch="False" />
                <line number="14" hits="22" branch="True" condition-coverage="100% (2/2)">
                  <conditions>
                    <condition number="57" type="jump" coverage="100%" />
                  </conditions>
                </line>
                <line number="15" hits="7" branch="False" />
                <line number="16" hits="7" branch="True" condition-coverage="100% (2/2)">
                  <conditions>
                    <condition number="27" type="jump" coverage="100%" />
                  </conditions>
                </line>
                <line number="17" hits="4" branch="False" />
                <line number="18" hits="4" branch="False" />
                <line number="20" hits="3" branch="False" />
                <line number="21" hits="4" branch="False" />
                <line number="23" hits="11" branch="False" />
              </lines>
            </method>
          </methods>
          <lines>
            <line number="8" hits="11" branch="False" />
            <line number="9" hits="11" branch="True" condition-coverage="100% (2/2)">
              <conditions>
                <condition number="7" type="jump" coverage="100%" />
              </conditions>
            </line>
            <line number="10" hits="3" branch="False" />
            <line number="11" hits="3" branch="False" />
            <line number="14" hits="22" branch="True" condition-coverage="100% (2/2)">
              <conditions>
                <condition number="57" type="jump" coverage="100%" />
              </conditions>
            </line>
            <line number="15" hits="7" branch="False" />
            <line number="16" hits="7" branch="True" condition-coverage="100% (2/2)">
              <conditions>
                <condition number="27" type="jump" coverage="100%" />
              </conditions>
            </line>
            <line number="17" hits="4" branch="False" />
            <line number="18" hits="4" branch="False" />
            <line number="20" hits="3" branch="False" />
            <line number="21" hits="4" branch="False" />
            <line number="23" hits="11" branch="False" />
          </lines>
        </class>
      </classes>
    </package>
  </packages>
</coverage>

Aanbeveling

Als alternatief kunt u het MSBuild-pakket gebruiken als uw buildsysteem al gebruikmaakt van MSBuild. Wijzig vanaf de opdrachtprompt de mappen in het XUnit.Coverlet.MSBuild-project en voer de dotnet test opdracht uit:

dotnet test /p:CollectCoverage=true /p:CoverletOutputFormat=cobertura

Het resulterende bestand coverage.cobertura.xml wordt uitgevoerd. Zie Coverlet-integratie met MSBuild voor meer informatie

Rapporten genereren

Nu u gegevens van eenheidstests kunt verzamelen, kunt u rapporten genereren met behulp van ReportGenerator. Als u het NuGet-pakket ReportGenerator wilt installeren als een algemeen .NET-hulpprogramma, gebruikt u de dotnet tool install opdracht:

dotnet tool install -g dotnet-reportgenerator-globaltool

Voer het hulpprogramma uit en geef de gewenste opties op met het uitvoerbestand coverage.cobertura.xml van de vorige testrun.

reportgenerator
-reports:"Path\To\TestProject\TestResults\{guid}\coverage.cobertura.xml"
-targetdir:"coveragereport"
-reporttypes:Html

Nadat u deze opdracht hebt uitgevoerd, vertegenwoordigt een HTML-bestand het gegenereerde rapport.

Rapport gegenereerd door unittests

Zie ook

Volgende stappen