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 verdergaan met de sectie Hulpprogramma's voor codedekking.

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 eenheidstests: regels, vertakkingen of methoden. Als u bijvoorbeeld een eenvoudige toepassing hebt met slechts twee voorwaardelijke vertakkingen van code (vertakking a en vertakking b), wordt een eenheidstest die controleert of voorwaardelijke vertakking wordt geverifieerd, de dekking van de vertakkingscode van 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 onder test 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 Class1.cs-bestand 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;
        }
    }
}

Tip

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 add package voeren:

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

Met de vorige opdracht zijn mappen effectief gewijzigd in het MSBuild-testproject en vervolgens het NuGet-pakket 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 nieuwe naam XUnit.Coverage voor het oplossingsbestand in de map UnitTestingCodeCoverage . Voeg de projecten toe aan de hoofdmap 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 waarnaar naar projecten en pakketten wordt verwezen en de broncode correct bijgewerkt. Klaar!

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.

Tip

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 beschrijvende naam die overeenkomt met de gegevensverzamelaars van Coverlet. Deze naam is vereist, maar is niet hoofdlettergevoelig. Als u wilt gebruiken. De ingebouwde codedekkingsgegevensverzamelaar van NET."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>

Tip

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 coverage.cobertura.xml bestand is uitvoer. U kunt hier de MSBuild-integratiehandleiding volgen

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, gezien de uitvoer coverage.cobertura.xml bestand uit de vorige testuitvoering.

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.

Unit test-generated report

Zie ook

Volgende stappen