Använda kodtäckning för enhetstestning
Viktigt!
I den här artikeln beskrivs hur du skapar exempelprojektet. Om du redan har ett projekt kan du gå vidare till avsnittet Kodtäckningsverktyg .
Enhetstester hjälper till att säkerställa funktionalitet och tillhandahålla ett sätt att verifiera refaktoriseringsarbetet. Kodtäckning är ett mått på mängden kod som körs av enhetstester – antingen rader, grenar eller metoder. Om du till exempel har ett enkelt program med endast två villkorsstyrda grenar av kod (gren a och gren b), rapporterar ett enhetstest som verifierar villkorsstyrd gren en förgreningskodtäckning på 50 %.
I den här artikeln beskrivs användningen av kodtäckning för enhetstestning med Coverlet och rapportgenerering med Hjälp av ReportGenerator. Den här artikeln fokuserar på C# och xUnit som testramverk, men både MSTest och NUnit fungerar också. Coverlet är ett öppen källkod projekt på GitHub som tillhandahåller ett plattformsoberoende kodtäckningsramverk för C#. Coverlet är en del av .NET Foundation. Coverlet samlar in Cobertura-täckningstestkörningsdata, som används för rapportgenerering.
Dessutom beskriver den här artikeln hur du använder kodtäckningsinformationen som samlas in från en Coverlet-testkörning för att generera en rapport. Rapportgenereringen är möjlig med hjälp av ett annat öppen källkod projekt på GitHub – ReportGenerator. ReportGenerator konverterar täckningsrapporter som genereras av Cobertura bland många andra, till rapporter som kan läsas av människor i olika format.
Den här artikeln baseras på exempelkällkodsprojektet, som är tillgängligt i exempelwebbläsaren.
System under test
"Systemet som testas" refererar till den kod som du skriver enhetstester mot, detta kan vara ett objekt, en tjänst eller något annat som exponerar testbara funktioner. I den här artikeln skapar du ett klassbibliotek som ska vara systemet som testas och två motsvarande enhetstestprojekt.
Skapa ett klassbibliotek
Från en kommandotolk i en ny katalog med namnet UnitTestingCodeCoverage
skapar du ett nytt .NET-standardklassbibliotek med kommandot dotnet new classlib
:
dotnet new classlib -n Numbers
Kodfragmentet nedan definierar en enkel PrimeService
klass som tillhandahåller funktioner för att kontrollera om ett tal är primärt. Kopiera kodfragmentet nedan och ersätt innehållet i den Class1.cs fil som skapades automatiskt i katalogen Numbers . Byt namn på filen Class1.cs till 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;
}
}
}
Dricks
Det är värt att nämna att Numbers
klassbiblioteket avsiktligt lades till i System
namnområdet. Detta gör det möjligt System.Math att vara tillgänglig utan en using System;
namnområdesdeklaration. Mer information finns i namnområdet (C#-referens).
Skapa testprojekt
Skapa två nya xUnit Test Project-mallar (.NET Core) från samma kommandotolk med kommandot dotnet new xunit
:
dotnet new xunit -n XUnit.Coverlet.Collector
dotnet new xunit -n XUnit.Coverlet.MSBuild
Båda de nyligen skapade xUnit-testprojekten måste lägga till en projektreferens för klassbiblioteket Numbers . Detta gör att testprojekten har åtkomst till PrimeService för testning. Använd kommandot i kommandotolken dotnet add
:
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
MSBuild-projektet namnges korrekt, eftersom det beror på coverlet.msbuild NuGet-paketet. Lägg till det här paketberoendet genom att dotnet add package
köra kommandot:
cd XUnit.Coverlet.MSBuild && dotnet add package coverlet.msbuild && cd ..
Det tidigare kommandot ändrade kataloger som effektivt omfångade till MSBuild-testprojektet och lade sedan till NuGet-paketet. När det var klart ändrade det sedan kataloger och stegade upp en nivå.
Öppna båda UnitTest1.cs-filerna och ersätt innehållet med följande kodfragment. Byt namn på UnitTest1.cs filer till 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");
}
}
Skapa en lösning
Från kommandotolken skapar du en ny lösning för att kapsla in klassbiblioteket och de två testprojekten. Med kommandot dotnet sln
:
dotnet new sln -n XUnit.Coverage
Då skapas ett nytt lösningsfilnamn XUnit.Coverage
i katalogen UnitTestingCodeCoverage . Lägg till projekten i lösningens rot.
Skapa lösningen med dotnet build
kommandot :
dotnet build
Om bygget lyckas har du skapat de tre projekten, refererat till projekt och paket på rätt sätt och uppdaterat källkoden korrekt. Bra gjort!
Kodtäckningsverktyg
Det finns två typer av kodtäckningsverktyg:
- DataCollectors: DataCollectors övervakar testkörning och samlar in information om testkörningar. De rapporterar den insamlade informationen i olika utdataformat, till exempel XML och JSON. Mer information finns i din första DataCollector.
- Rapportgeneratorer: Använd data som samlats in från testkörningar för att generera rapporter, ofta som formaterad HTML.
I det här avsnittet fokuserar vi på datainsamlarverktyg.
.NET innehåller en inbyggd datainsamlare för kodtäckning, som också är tillgänglig i Visual Studio. Den här datainsamlaren genererar en binär .coverage-fil som kan användas för att generera rapporter i Visual Studio. Den binära filen är inte läsbar för människor och måste konverteras till ett läsbart format för människor innan den kan användas för att generera rapporter utanför Visual Studio.
Dricks
Verktyget dotnet-coverage
är ett plattformsoberoende verktyg som kan användas för att konvertera testresultatfilen för binär täckning till ett läsbart format för människor. Mer information finns i dotnet-coverage.
Coverlet är ett alternativ med öppen källkod till den inbyggda insamlaren. Den genererar testresultat som läsbara Cobertura XML-filer som sedan kan användas för att generera HTML-rapporter. Om du vill använda Coverlet för kodtäckning måste ett befintligt enhetstestprojekt ha rätt paketberoenden, eller alternativt förlita sig på globala .NET-verktyg och motsvarande Coverlet.console NuGet-paket.
Integrera med .NET-test
XUnit-testprojektmallen integreras redan med coverlet.collector som standard.
Från kommandotolken ändrar du kataloger till projektet XUnit.Coverlet.Collector och kör dotnet test
kommandot:
cd XUnit.Coverlet.Collector && dotnet test --collect:"XPlat Code Coverage"
Kommentar
Argumentet "XPlat Code Coverage"
är ett eget namn som motsvarar datainsamlaren från Coverlet. Det här namnet krävs men är skiftlägesokänsligt. Så här använder du . NET:s inbyggda datainsamlare för kodtäckning använder "Code Coverage"
.
Som en del av körningen dotnet test
matas en resulterande coverage.cobertura.xml fil ut till katalogen TestResults . XML-filen innehåller resultatet. Det här är ett plattformsoberoende alternativ som förlitar sig på .NET CLI, och det är bra för byggsystem där MSBuild inte är tillgängligt.
Nedan visas exemplet coverage.cobertura.xml fil.
<?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>
Dricks
Alternativt kan du använda MSBuild-paketet om byggsystemet redan använder MSBuild. Från kommandotolken ändrar du kataloger till projektet XUnit.Coverlet.MSBuild och kör dotnet test
kommandot:
dotnet test /p:CollectCoverage=true /p:CoverletOutputFormat=cobertura
Den resulterande coverage.cobertura.xml filen är utdata. Du kan följa MSBuild-integreringsguiden här
Generera rapporter
Nu när du kan samla in data från enhetstestkörningar kan du generera rapporter med Hjälp av ReportGenerator. Om du vill installera ReportGenerator NuGet-paketet som ett globalt .NET-verktyg använder du dotnet tool install
kommandot:
dotnet tool install -g dotnet-reportgenerator-globaltool
Kör verktyget och ange önskade alternativ med tanke på utdata coverage.cobertura.xml fil från föregående testkörning.
reportgenerator
-reports:"Path\To\TestProject\TestResults\{guid}\coverage.cobertura.xml"
-targetdir:"coveragereport"
-reporttypes:Html
När du har kört det här kommandot representerar en HTML-fil den genererade rapporten.
Se även
- Visual Studio-enhetens testkodtäckning
- GitHub – Coverlet-lagringsplats
- GitHub – ReportGenerator-lagringsplats
- ReportGenerator-projektwebbplats
- Azure: Publicera resultat för kodtäckning
- Azure: Granska resultat av kodtäckning
- .NET CLI-testkommando
- dotnet-täckning
- Exempel på källkod