Przewodnik: tworzenie i uruchamianie testów jednostkowych dla kodu zarządzanego
W tym artykule przedstawiono procedurę tworzenia, uruchamiania i dostosowywania serii testów jednostkowych przy użyciu struktury testów jednostkowych firmy Microsoft dla kodu zarządzanego i Eksploratora testów programu Visual Studio. Zaczynasz od projektu w języku C#, który jest opracowywany, tworzysz testy, które wykonują jego kod, uruchamiasz testy i sprawdzasz wyniki. Następnie zmienisz kod projektu i ponownie uruchomisz testy. Jeśli chcesz zapoznać się z koncepcyjnym omówieniem tych zadań przed wykonaniem tych kroków, zobacz Podstawy testu jednostkowego. Jeśli chcesz automatycznie wygenerować testy na podstawie istniejącego kodu, zobacz Tworzenie wycinków metody testu jednostkowego na podstawie kodu.
Tworzenie projektu do przetestowania
Otwórz program Visual Studio.
W oknie uruchamiania wybierz pozycję Utwórz nowy projekt.
Wyszukaj i wybierz szablon projektu Aplikacja konsolowa języka C# dla platformy .NET, a następnie kliknij przycisk Dalej.
Uwaga
Jeśli nie widzisz szablonu Aplikacja konsolowa, możesz zainstalować go w oknie Tworzenie nowego projektu . W komunikacie Nie można znaleźć tego, czego szukasz? wybierz link Zainstaluj więcej narzędzi i funkcji. Następnie w Instalator programu Visual Studio wybierz obciążenie programowanie aplikacji klasycznych platformy .NET.
Nadaj projektowi nazwę Bank, a następnie kliknij przycisk Dalej.
Wybierz zalecaną strukturę docelową lub platformę .NET 8, a następnie wybierz pozycję Utwórz.
Projekt Bank jest tworzony i wyświetlany w Eksplorator rozwiązań przy użyciu pliku Program.cs otwartego w edytorze kodu.
Uwaga
Jeśli Program.cs nie jest otwarty w edytorze, kliknij dwukrotnie plik Program.cs w Eksplorator rozwiązań, aby go otworzyć.
Zastąp zawartość Program.cs następującym kodem c#, który definiuje klasę 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); } } }
Zmień nazwę pliku na BankAccount.cs, klikając prawym przyciskiem myszy i wybierając polecenie Zmień nazwę w Eksplorator rozwiązań.
W menu Kompilacja kliknij pozycję Kompiluj rozwiązanie (lub naciśnij Ctrl + SHIFT + B).
Masz teraz projekt z metodami, które można przetestować. W tym artykule testy koncentrują się na metodzie Debit
. Metoda jest wywoływana Debit
, gdy pieniądze są wycofane z konta.
Tworzenie projektu testów jednostkowych
W menu Plik wybierz pozycję Dodaj>nowy projekt.
Napiwek
Możesz również kliknąć rozwiązanie prawym przyciskiem myszy w Eksplorator rozwiązań i wybrać polecenie Dodaj>nowy projekt.
Wpisz test w polu wyszukiwania, wybierz język C# jako język, a następnie wybierz szablon C# MSTest Unit Test Project for .NET, a następnie kliknij przycisk Dalej.
Uwaga
W programie Visual Studio 2019 w wersji 16.9 szablon projektu MSTest jest projektem testów jednostkowych.
Nadaj projektowi nazwę BankTests i kliknij przycisk Dalej.
Wybierz zalecaną strukturę docelową lub platformę .NET 8, a następnie wybierz pozycję Utwórz.
Projekt BankTests jest dodawany do rozwiązania Bank .
W projekcie BankTests dodaj odwołanie do projektu Bank .
W Eksplorator rozwiązań wybierz pozycję Zależności w projekcie BankTests, a następnie wybierz pozycję Dodaj odwołanie (lub Dodaj odwołanie do projektu) z menu prawym przyciskiem myszy.
W oknie dialogowym Menedżer odwołań rozwiń węzeł Projekty, wybierz pozycję Rozwiązanie, a następnie zaznacz element Bank.
Wybierz pozycję OK.
Tworzenie klasy testowej
Utwórz klasę testową, aby zweryfikować klasę BankAccount
. Możesz użyć pliku UnitTest1.cs wygenerowanego przez szablon projektu, ale nadaj plikowi i klasie bardziej opisowe nazwy.
Zmienianie nazwy pliku i klasy
Aby zmienić nazwę pliku, w Eksplorator rozwiązań wybierz plik UnitTest1.cs w projekcie BankTests. W menu prawym przyciskiem myszy wybierz polecenie Zmień nazwę (lub naciśnij F2), a następnie zmień nazwę pliku na BankAccountTests.cs.
Aby zmienić nazwę klasy, umieść kursor w
UnitTest1
edytorze kodu, kliknij prawym przyciskiem myszy, a następnie wybierz polecenie Zmień nazwę (lub naciśnij F2). Wpisz ciąg BankAccountTests , a następnie naciśnij Enter.
Plik BankAccountTests.cs zawiera teraz następujący 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()
{
}
}
}
Dodawanie instrukcji using
Dodaj instrukcję using
do klasy testowej, aby umożliwić wywołanie do projektu w ramach testu bez używania w pełni kwalifikowanych nazw. W górnej części pliku klasy dodaj:
using BankAccountNS;
Wymagania dotyczące klasy testowej
Minimalne wymagania dla klasy testowej to:
Atrybut
[TestClass]
jest wymagany w dowolnej klasie zawierającej metody testów jednostkowych, które mają być uruchamiane w Eksploratorze testów.Każda metoda testowa, którą ma rozpoznać Eksplorator testów, musi mieć
[TestMethod]
atrybut .
Można mieć inne klasy w projekcie testów jednostkowych, które nie mają atrybutu [TestClass]
, i można mieć inne metody w klasach testowych, które nie mają atrybutu [TestMethod]
. Można wywołać te inne klasy i metody z metod testowych.
Tworzenie pierwszej metody testowej
W tej procedurze piszesz metody testów jednostkowych w celu zweryfikowania zachowania Debit
metody BankAccount
klasy.
Należy sprawdzić co najmniej trzy zachowania:
Metoda zgłasza wartość ArgumentOutOfRangeException , jeśli kwota debetowa jest większa niż saldo.
Metoda zgłasza wartość ArgumentOutOfRangeException , jeśli kwota debetowa jest mniejsza niż zero.
Jeśli kwota debetowa jest prawidłowa, metoda odejmuje kwotę debetową z salda konta.
Napiwek
Możesz usunąć metodę domyślną TestMethod1
, ponieważ nie będzie jej używać w tym przewodniku.
Aby utworzyć metodę testową
Pierwszy test sprawdza, czy prawidłowa kwota (czyli ta, która jest mniejsza niż saldo konta i większa niż zero) wycofuje prawidłową kwotę z konta. Dodaj następującą metodę do tej BankAccountTests
klasy:
[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");
}
Metoda jest prosta: konfiguruje nowy BankAccount
obiekt z początkowym saldem, a następnie wycofuje prawidłową kwotę. Używa Assert.AreEqual metody , aby sprawdzić, czy saldo końcowe jest zgodnie z oczekiwaniami. Metody, takie jak Assert.AreEqual
, Assert.IsTruei inne, są często używane w testach jednostkowych. Aby uzyskać więcej informacji koncepcyjnych na temat pisania testu jednostkowego, zobacz Pisanie testów.
Wymagania dotyczące metody testowania
Metoda testowa musi spełniać następujące wymagania:
Jest on ozdobiony atrybutem
[TestMethod]
.Zwraca wartość
void
.Nie może mieć parametrów.
Kompilowanie i uruchamianie testu
W menu Kompilacja wybierz pozycję Kompiluj rozwiązanie (lub naciśnij Ctrl + SHIFT + B).
Jeśli Eksplorator testów nie jest otwarty, otwórz go, wybierając pozycję Eksplorator testów testowych>(lub> Eksplorator testów systemu Windows>) z górnego paska menu (lub naciśnij Ctrl + E, T).
Wybierz pozycję Uruchom wszystko , aby uruchomić test (lub naciśnij Ctrl + R, V).
Gdy test jest uruchomiony, pasek stanu w górnej części okna Eksplorator testów jest animowany. Na końcu przebiegu testu pasek zmieni kolor na zielony, jeśli wszystkie metody testowe zakończą się powodzeniem lub czerwony, jeśli którykolwiek z testów zakończy się niepowodzeniem.
W takim przypadku test zakończy się niepowodzeniem.
Wybierz metodę w Eksploratorze testów, aby wyświetlić szczegóły w dolnej części okna.
Naprawianie kodu i ponowne uruchamianie testów
Wynik testu zawiera komunikat opisujący błąd. Może być konieczne przejście do szczegółów, aby zobaczyć ten komunikat. W przypadku AreEqual
metody komunikat wyświetla oczekiwane wartości i faktycznie odebrane. Oczekujesz, że saldo spadnie, ale zamiast tego zwiększy się o kwotę wypłaty.
Test jednostkowy wykrył usterkę: kwota wypłaty jest dodawana do salda konta, gdy ma zostać odjęta.
Poprawianie usterki
Aby naprawić błąd, w pliku BankAccount.cs zastąp wiersz:
m_balance += amount;
tym:
m_balance -= amount;
Ponowne uruchamianie testu
W Eksploratorze testów wybierz pozycję Uruchom wszystko, aby ponownie uruchomić test (lub naciśnij Ctrl + R, V). Czerwony/zielony pasek zmieni kolor na zielony, aby wskazać, że test zakończył się pomyślnie.
Ulepszanie kodu przy użyciu testów jednostkowych
W tej sekcji opisano, jak iteracyjny proces analizy, programowania testów jednostkowych i refaktoryzacji może pomóc zwiększyć niezawodność i efektywność kodu produkcyjnego.
Analizowanie problemów
Utworzono metodę testową, aby potwierdzić, że prawidłowa kwota jest poprawnie odjęta w metodzie Debit
. Teraz sprawdź, czy metoda zgłasza wartość ArgumentOutOfRangeException , jeśli kwota debetowa to:
- większe niż saldo lub
- mniejsze niż zero.
Tworzenie i uruchamianie nowych metod testowania
Utwórz metodę testową, aby sprawdzić prawidłowe zachowanie, gdy kwota debetowa jest mniejsza niż zero:
[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));
}
ThrowsException Użyj metody , aby potwierdzić, że został zgłoszony prawidłowy wyjątek. Ta metoda powoduje niepowodzenie testu, chyba że zostanie zgłoszony błąd ArgumentOutOfRangeException . Jeśli tymczasowo zmodyfikujesz metodę testową, aby zgłosić bardziej ogólną ApplicationException wartość, gdy kwota debetowa jest mniejsza niż zero, test działa prawidłowo — oznacza to, że kończy się niepowodzeniem.
Aby przetestować przypadek, gdy kwota wycofana jest większa niż saldo, wykonaj następujące czynności:
Utwórz nową metodę testową o nazwie
Debit_WhenAmountIsMoreThanBalance_ShouldThrowArgumentOutOfRange
.Skopiuj treść metody z
Debit_WhenAmountIsLessThanZero_ShouldThrowArgumentOutOfRange
do nowej metody.debitAmount
Ustaw wartość na liczbę większą niż saldo.
Uruchom dwa testy i sprawdź, czy zostały one pomyślnie wykonane.
Kontynuuj analizę
Testowana metoda może zostać ulepszona. W przypadku bieżącej implementacji nie mamy możliwości sprawdzenia, który warunek (amount > m_balance
lub amount < 0
) spowodował zgłoszenie wyjątku podczas testu. Wiemy tylko, że ArgumentOutOfRangeException
element został rzucony gdzieś w metodzie. Byłoby lepiej, gdybyśmy mogli stwierdzić, który warunek w BankAccount.Debit
przyczynie wyjątku został zgłoszony (amount > m_balance
lub amount < 0
), abyśmy mogli mieć pewność, że nasza metoda prawidłowo sprawdza jego argumenty.
Ponownie przyjrzyj się testowanej metodzie (BankAccount.Debit
) i zwróć uwagę, że obie instrukcje warunkowe używają ArgumentOutOfRangeException
konstruktora, który po prostu przyjmuje nazwę argumentu jako parametr:
throw new ArgumentOutOfRangeException("amount");
Istnieje konstruktor, którego można użyć, aby raportować znacznie bogatsze informacje: ArgumentOutOfRangeException(String, Object, String) zawiera nazwę argumentu, wartość argumentu i komunikat zdefiniowany przez użytkownika. Możesz refaktoryzować metodę testową, aby użyć tego konstruktora. Jeszcze lepiej, można użyć publicznie dostępnych składowych typów, aby określić błędy.
Refaktoryzacja kodu testowego
Najpierw zdefiniuj dwie stałe dla komunikatów o błędach w zakresie klasy. Umieść definicje w klasie testowej: BankAccount
public const string DebitAmountExceedsBalanceMessage = "Debit amount exceeds balance";
public const string DebitAmountLessThanZeroMessage = "Debit amount is less than zero";
Następnie zmodyfikuj dwie instrukcje warunkowe w metodzie Debit
:
if (amount > m_balance)
{
throw new System.ArgumentOutOfRangeException("amount", amount, DebitAmountExceedsBalanceMessage);
}
if (amount < 0)
{
throw new System.ArgumentOutOfRangeException("amount", amount, DebitAmountLessThanZeroMessage);
}
Refaktoryzacja metod testowych
Refaktoryzuj metody testowe, usuwając wywołanie metody Assert.ThrowsException. Zawijanie wywołania w Debit()
bloku, przechwycenie oczekiwanego try/catch
wyjątku i zweryfikowanie skojarzonego z nim komunikatu. Metoda Microsoft.VisualStudio.TestTools.UnitTesting.StringAssert.Contains umożliwia porównywanie dwóch ciągów.
Debit_WhenAmountIsMoreThanBalance_ShouldThrowArgumentOutOfRange
Teraz element może wyglądać następująco:
[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);
}
}
Ponowne testowanie, ponowne zapisywanie i ponowne analiza
Obecnie metoda testowa nie obsługuje wszystkich przypadków, które powinny. Jeśli metoda testowana Debit
, metoda nie zgłosiła ArgumentOutOfRangeException wartości, gdy debitAmount
wartość była większa niż saldo (lub mniejsza niż zero), metoda testowa przejdzie pomyślnie. Ten scenariusz nie jest dobry, ponieważ chcesz, aby metoda testowa nie powiodła się, jeśli nie zostanie zgłoszony wyjątek.
Ten wynik jest usterką w metodzie testowej. Aby rozwiązać ten problem, dodaj asercję Assert.Fail na końcu metody testowej, aby obsłużyć przypadek, w którym nie jest zgłaszany żaden wyjątek.
Ponowne uruchomienie testu pokazuje, że test zakończy się niepowodzeniem, jeśli zostanie przechwycony prawidłowy wyjątek. Blok catch
przechwytuje wyjątek, ale metoda nadal jest wykonywana i kończy się niepowodzeniem w nowej Assert.Fail asercji. Aby rozwiązać ten problem, dodaj instrukcję return
po StringAssert
catch
bloku . Ponowne uruchomienie testu potwierdza, że rozwiązano ten problem. Ostateczna wersja elementu Debit_WhenAmountIsMoreThanBalance_ShouldThrowArgumentOutOfRange
wygląda następująco:
[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.");
}
Podsumowanie
Ulepszenia kodu testowego doprowadziły do bardziej niezawodnych i informacyjnych metod testowania. Co ważniejsze, ulepszyli również kod testowy.
Napiwek
W tym przewodniku użyto struktury testów jednostkowych firmy Microsoft dla kodu zarządzanego. Eksplorator testów może również uruchamiać testy z platform testów jednostkowych innych firm, które mają karty dla Eksploratora testów. Aby uzyskać więcej informacji, zobacz Instalowanie platform testów jednostkowych innych firm.
Powiązana zawartość
Aby uzyskać informacje na temat uruchamiania testów z poziomu wiersza polecenia, zobacz VSTest.Console.exe opcje wiersza polecenia.