Genomgång: Skapa och köra enhetstester för hanterad kod
Den här artikeln beskriver hur du skapar, kör och anpassar en serie enhetstester med hjälp av Microsofts enhetstestramverk för hanterad kod och Visual Studio Test Explorer. Du börjar med ett C#-projekt som är under utveckling, skapar tester som tränar koden, kör testerna och undersöker resultaten. Sedan ändrar du projektkoden och kör testerna igen. Om du vill ha en konceptuell översikt över dessa uppgifter innan du går igenom de här stegen kan du läsa grunderna i Enhetstest. Om du vill generera tester automatiskt från befintlig kod kan du läsa Skapa enhetstestmetodsstubbar från kod.
Skapa ett projekt att testa
Öppna Visual Studio.
I startfönstret väljer du Skapa ett nytt projekt.
Sök efter och välj projektmallen C# Console App för .NET och klicka sedan på Nästa.
Observera
Om du inte ser mallen Console App kan du installera den från fönstret Skapa ett nytt projekt. I Hittar du inte det du letar efter? meddelande väljer du länken Installera fler verktyg och funktioner. Välj sedan arbetsbelastningen .NET Desktop Development i Visual Studio Installer.
Ge projektet namnet Bankoch klicka sedan på Nästa.
Välj antingen det rekommenderade målramverket eller .NET 8 och välj sedan Skapa.
Bankprojektet skapas och visas i Solution Explorer med filen Program.cs öppen i kodredigeraren.
Not
Om Program.cs inte är öppen i redigeraren dubbelklickar du på filen Program.cs i Solution Explorer för att öppna den.
Ersätt innehållet i Program.cs med följande C#-kod som definierar en klass, BankAccount:
using System; namespace BankAccountNS { /// <summary> /// Bank account demo class. /// </summary> public class BankAccount { private readonly string m_customerName; private double m_balance; private BankAccount() { } public BankAccount(string customerName, double balance) { m_customerName = customerName; m_balance = balance; } public string CustomerName { get { return m_customerName; } } public double Balance { get { return m_balance; } } public void Debit(double amount) { if (amount > m_balance) { throw new ArgumentOutOfRangeException("amount"); } if (amount < 0) { throw new ArgumentOutOfRangeException("amount"); } m_balance += amount; // intentionally incorrect code } public void Credit(double amount) { if (amount < 0) { throw new ArgumentOutOfRangeException("amount"); } m_balance += amount; } public static void Main() { BankAccount ba = new BankAccount("Mr. Bryan Walton", 11.99); ba.Credit(5.77); ba.Debit(11.22); Console.WriteLine("Current balance is ${0}", ba.Balance); } } }
Byt namn på filen till BankAccount.cs genom att högerklicka och välja Byt namn på i Solution Explorer.
På menyn Build klickar du på Build Solution (eller trycker på Ctrl + SKIFT + B).
Nu har du ett projekt med metoder som du kan testa. I den här artikeln fokuserar testerna på metoden Debit
. Metoden Debit
anropas när pengar tas ut från ett konto.
Skapa ett enhetstestprojekt
På menyn Arkiv väljer du Lägg till>Nytt projekt.
Tips
Du kan också högerklicka på lösningen i Solution Explorer och välja Lägg till>Nytt projekt.
Skriv test i sökrutan, välj C# som språk och välj sedan C# MSTest Test Project för .NET-mallen och klicka sedan på Nästa.
Notera
I Visual Studio 2019 version 16.9 är MSTest-projektmallen Unit Test Project.
Ge projektet namnet BankTests och klicka på Nästa.
Välj antingen det rekommenderade målramverket eller .NET 8 och välj sedan Skapa.
Från och med Visual Studio 2022 version 17.10 kan du också välja en testlöpare. För testkörare kan du välja antingen VSTest eller MSTest. Mer information om skillnaden mellan testlöpare finns i Microsoft.Testing.Platform och VSTest-jämförelsen.
Projektet BankTests läggs till i Bank-lösningen.
I projektet BankTests lägger du till en referens till projektet Bank.
I Solution Explorerväljer du Beroenden under projektet BankTests och väljer sedan Lägg till referens (eller Lägg till projektreferens) på högerklicksmenyn.
I dialogrutan Reference Manager expanderar du Projects, väljer Solutionoch markerar sedan objektet Bank.
Välj OK.
Skapa testklassen
Skapa en testklass för att verifiera klassen BankAccount
. Du kan använda UnitTest1.cs-filen som genererades av projektmallen, men ge filen och klassen mer beskrivande namn.
Byt namn på en fil och klass
Om du vill byta namn på filen i Solution Explorerväljer du filen UnitTest1.cs i BankTests-projektet. På högerklicksmenyn väljer du Byt namn på (eller trycker på F2) och byter sedan namn på filen till BankAccountTests.cs.
Om du vill byta namn på klassen placerar du markören på
UnitTest1
i kodredigeraren, högerklickar och väljer sedan Byt namn på (eller trycker på F2). Skriv in BankAccountTests och tryck sedan på Retur.
Filen BankAccountTests.cs innehåller nu följande kod:
// The 'using' statement for Test Tools is in GlobalUsings.cs
// using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace BankTests
{
[TestClass]
public class BankAccountTests
{
[TestMethod]
public void TestMethod1()
{
}
}
}
Lägg till en 'using'-instruktion
Lägg till en using
-instruktion till testklassen för att kunna anropa det testade projektet utan att använda fullständigt kvalificerade namn. Överst i klassfilen lägger du till:
using BankAccountNS;
Krav för testklass
Minimikraven för en testklass är:
Attributet
[TestClass]
krävs för alla klasser som innehåller enhetstestmetoder som du vill köra i TestUtforskaren.Varje testmetod som du vill att Test Explorer ska känna igen måste ha attributet
[TestMethod]
.
Du kan ha andra klasser i ett enhetstestprojekt som inte har attributet [TestClass]
och du kan ha andra metoder i testklasser som inte har attributet [TestMethod]
. Du kan anropa dessa andra klasser och metoder från dina testmetoder.
Skapa den första testmetoden
I den här proceduren skriver du enhetstestmetoder för att verifiera beteendet för Debit
-metoden i klassen BankAccount
.
Det finns minst tre beteenden som måste kontrolleras:
Metoden genererar en ArgumentOutOfRangeException om debetbeloppet är större än saldot.
Metoden genererar en ArgumentOutOfRangeException om debetbeloppet är mindre än noll.
Om debetbeloppet är giltigt subtraherar metoden debetbeloppet från kontosaldot.
Tips
Du kan ta bort standardmetoden TestMethod1
eftersom du inte använder den i den här genomgången.
Så här skapar du en testmetod
Det första testet verifierar att ett giltigt belopp (det vill säga ett belopp som är mindre än kontosaldot och större än noll) tar ut rätt belopp från kontot. Lägg till följande metod i den BankAccountTests
klassen:
[TestMethod]
public void Debit_WithValidAmount_UpdatesBalance()
{
// Arrange
double beginningBalance = 11.99;
double debitAmount = 4.55;
double expected = 7.44;
BankAccount account = new BankAccount("Mr. Bryan Walton", beginningBalance);
// Act
account.Debit(debitAmount);
// Assert
double actual = account.Balance;
Assert.AreEqual(expected, actual, 0.001, "Account not debited correctly");
}
Metoden är enkel: den konfigurerar ett nytt BankAccount
objekt med ett startsaldo och tar sedan ut ett giltigt belopp. Den använder metoden Assert.AreEqual för att verifiera att slutsaldot är som förväntat. Metoder som Assert.AreEqual
, Assert.IsTrueoch andra används ofta i enhetstestning. Mer konceptuell information om hur du skriver ett enhetstest finns i Skriva dina tester.
Krav för testmetod
En testmetod måste uppfylla följande krav:
Den är dekorerad med attributet
[TestMethod]
.Den returnerar
void
.Den kan inte ha parametrar.
Skapa och köra testet
På menyn Build väljer du Build Solution (eller trycker på Ctrl + SKIFT + B).
Om Test Explorer inte är öppen öppnar du den genom att välja Test>Test Explorer (eller Test>Windows>Test Explorer) i det översta menyfältet (eller tryck på Ctrl + E, T).
Välj Kör alla för att köra testet (eller tryck på Ctrl + R, V).
När testet körs animerats statusfältet överst i Test Explorer-fönstret. I slutet av testkörningen blir fältet grönt om alla testmetoder godkänns eller om något av testerna misslyckas.
I det här fallet misslyckas testet.
Välj metoden i Test Explorer för att visa informationen längst ned i fönstret.
Åtgärda koden och kör testerna igen
Testresultatet innehåller ett meddelande som beskriver felet. Du kan behöva öka detaljnivån för att se det här meddelandet. För AreEqual
-metoden visar meddelandet vad som förväntades och vad som faktiskt togs emot. Du förväntade dig att saldot skulle minska, men i stället ökade det med beloppet för uttag.
Enhetstestet har upptäckt ett fel: uttagsbeloppet läggs till kontosaldot när det istället borde subtraherasfrån kontosaldot.
Korrigera buggen
Om du vill korrigera felet ersätter du raden i filen BankAccount.cs:
m_balance += amount;
med:
m_balance -= amount;
Kör testet igen
I Test Explorerväljer du Kör alla för att köra testet igen (eller tryck på Ctrl + R, V). Den röda/gröna stapeln blir grön för att indikera att testet har godkänts.
Använda enhetstester för att förbättra koden
Det här avsnittet beskriver hur en iterativ analysprocess, enhetstestutveckling och refaktorisering kan hjälpa dig att göra din produktionskod mer robust och effektiv.
Analysera problemen
Du har skapat en testmetod för att bekräfta att ett giltigt belopp dras av korrekt i metoden Debit
. Kontrollera nu att metoden genererar en ArgumentOutOfRangeException om debetbeloppet antingen är:
- större än saldot, eller
- mindre än noll.
Skapa och köra nya testmetoder
Skapa en testmetod för att verifiera korrekt beteende när debetbeloppet är mindre än noll:
[TestMethod]
public void Debit_WhenAmountIsLessThanZero_ShouldThrowArgumentOutOfRange()
{
// Arrange
double beginningBalance = 11.99;
double debitAmount = -100.00;
BankAccount account = new BankAccount("Mr. Bryan Walton", beginningBalance);
// Act and assert
Assert.ThrowsException<System.ArgumentOutOfRangeException>(() => account.Debit(debitAmount));
}
Använd metoden ThrowsException för att bekräfta att rätt undantag har genererats. Den här metoden gör att testet misslyckas om inte en ArgumentOutOfRangeException genereras. Om du tillfälligt ändrar metoden under test för att kasta ett mer generiskt ApplicationException när debetbeloppet är mindre än noll, fungerar testet korrekt, det vill säga, det misslyckas.
Gör följande för att testa fallet när det belopp som tas ut är större än saldot:
Skapa en ny testmetod med namnet
Debit_WhenAmountIsMoreThanBalance_ShouldThrowArgumentOutOfRange
.Kopiera metodtexten från
Debit_WhenAmountIsLessThanZero_ShouldThrowArgumentOutOfRange
till den nya metoden.Ange
debitAmount
till ett tal som är större än saldot.
Kör de två testerna och kontrollera att de har godkänts.
Fortsätt analysen
Metoden som testas kan förbättras ytterligare. Med den aktuella implementeringen vet vi inte vilket villkor (amount > m_balance
eller amount < 0
) som ledde till att undantaget utlöstes under testet. Vi vet bara att en ArgumentOutOfRangeException
utlöstes någonstans i metoden. Det skulle vara bättre om vi kunde se vilket villkor i BankAccount.Debit
som orsakade att undantaget utlöstes (amount > m_balance
eller amount < 0
), så vi kan vara säkra på att vår metod kontrollerar sina argument för rimlighet på rätt sätt.
Titta på metoden som testas (BankAccount.Debit
) igen och observera att båda villkorssatserna använder en ArgumentOutOfRangeException
konstruktor som bara tar namnet på argumentet som en parameter:
throw new ArgumentOutOfRangeException("amount");
Det finns en konstruktor som du kan använda som rapporterar mycket mer omfattande information: ArgumentOutOfRangeException(String, Object, String) innehåller namnet på argumentet, argumentvärdet och ett användardefinierat meddelande. Du kan omstrukturera metoden under test för att använda den här konstruktorn. Ännu bättre är att du kan använda offentligt tillgängliga typmedlemmar för att ange felen.
Omstrukturera den koden som testas
Definiera först två konstanter för felmeddelandena i klassomfånget. Placera definitionerna i klassen som testas, BankAccount
:
public const string DebitAmountExceedsBalanceMessage = "Debit amount exceeds balance";
public const string DebitAmountLessThanZeroMessage = "Debit amount is less than zero";
Ändra sedan de två villkorssatserna i metoden Debit
:
if (amount > m_balance)
{
throw new System.ArgumentOutOfRangeException("amount", amount, DebitAmountExceedsBalanceMessage);
}
if (amount < 0)
{
throw new System.ArgumentOutOfRangeException("amount", amount, DebitAmountLessThanZeroMessage);
}
Omstrukturera testmetoderna
Omstrukturera testmetoderna genom att ta bort anropet till Assert.ThrowsException. Omslut anropet till Debit()
i ett try/catch
-block, fånga det specifika undantaget som förväntas och verifiera det associerade meddelandet. Metoden Microsoft.VisualStudio.TestTools.UnitTesting.StringAssert.Contains ger möjlighet att jämföra två strängar.
Nu kan Debit_WhenAmountIsMoreThanBalance_ShouldThrowArgumentOutOfRange
se ut så här:
[TestMethod]
public void Debit_WhenAmountIsMoreThanBalance_ShouldThrowArgumentOutOfRange()
{
// Arrange
double beginningBalance = 11.99;
double debitAmount = 20.0;
BankAccount account = new BankAccount("Mr. Bryan Walton", beginningBalance);
// Act
try
{
account.Debit(debitAmount);
}
catch (System.ArgumentOutOfRangeException e)
{
// Assert
StringAssert.Contains(e.Message, BankAccount.DebitAmountExceedsBalanceMessage);
}
}
Testa, skriva om och analysera igen
För närvarande hanterar testmetoden inte alla fall som den bör. Om metoden under test, metoden Debit
, inte kunde utlösa en ArgumentOutOfRangeException när debitAmount
var större än saldot (eller mindre än noll), skulle testmetoden passera. Det här scenariot är inte bra eftersom du vill att testmetoden ska misslyckas om inget undantag utlöses.
Det här resultatet är en bugg i testmetoden. Lös problemet genom att lägga till ett Assert.Fail-påstående i slutet av testmetoden för att hantera det fall där inget undantag utlöses.
Om du kör testet igen visas att testet nu misslyckas om rätt undantag fångas. Det catch
-blocket fångar undantaget, men metoden fortsätter att köras och misslyckas sedan vid den nya Assert.Fail-asserten. Lös problemet genom att lägga till en return
-instruktion efter StringAssert
i catch
-blocket. Om du kör testet igen bekräftas att du har åtgärdat det här problemet. Den slutliga versionen av Debit_WhenAmountIsMoreThanBalance_ShouldThrowArgumentOutOfRange
ser ut så här:
[TestMethod]
public void Debit_WhenAmountIsMoreThanBalance_ShouldThrowArgumentOutOfRange()
{
// Arrange
double beginningBalance = 11.99;
double debitAmount = 20.0;
BankAccount account = new BankAccount("Mr. Bryan Walton", beginningBalance);
// Act
try
{
account.Debit(debitAmount);
}
catch (System.ArgumentOutOfRangeException e)
{
// Assert
StringAssert.Contains(e.Message, BankAccount.DebitAmountExceedsBalanceMessage);
return;
}
Assert.Fail("The expected exception was not thrown.");
}
Slutsats
Förbättringarna av testkoden ledde till mer robusta och informativa testmetoder. Men ännu viktigare är att de också förbättrade koden under test.
Tips
Den här genomgången använder Microsoft-enhetstestramverket för hanterad kod. Test Explorer kan också köra tester från enhetstestningsramverk från tredje part som har adaptrar för Test Explorer. Mer information finns i Installera testramverk från tredje part.
Relaterat innehåll
Information om hur du kör tester från en kommandorad finns i VSTest.Console.exe kommandoradsalternativ.