Wskazówki: tworzenie i uruchamianie testów jednostkowych zarządzanego kodu
W tym przewodniku zawarte są instrukcje jak krok po kroku tworzyć, uruchamiać i dostosowywać serie testów jednostkowych przy użyciu frameworka testów jednostkowych firmy Microsoft dla kodu zarządzanego i programu Visual Studio Test Explorer.Rozpoczyna się projektem w języku C#, który jest w rozwoju, tworzy testy, które wykonują kod, uruchamia testy i bada wyniki.Następnie można zmienić kodu projektu i ponownie uruchomić testy.
Ten temat zawiera następujące sekcje:
Utwórz projekt testów jednostkowych
Tworzenie pierwszej metody testowej
Napraw kod i uruchom ponownie testy
Użyj testów jednostkowych aby poprawić kod
[!UWAGA]
W tym przewodniku używa się frameworka testów jednostkowych firmy Microsoft dla kodu zarządzanego.Można również uruchomić testy, za pomocą programu Test Explorer, ze struktury testów jednostkowych innej firmy, która posiada adaptery dla programu Test Explorer.Aby uzyskać więcej informacji, zobacz Jak: Instalowanie RAM Test jednostki strony trzeciej.
[!UWAGA]
Aby uzyskać więcej informacji o sposobach uruchamiania testów z wiersza polecenia, zobacz Instruktaż: Za pomocą narzędzia wiersza polecenia badania.
Wymagania wstępne
- Projekt o nazwie Bank.Zobacz Przykładowy projekt dotyczący tworzenia testów jednostkowych.
Przygotuj przewodnik
Aby przygotować przewodnik
Otwórz Visual Studio 2012.
W menu Plik, wskaż Nowy i kliknij przycisk Projekt.
Pojawi się okno dialogowe Nowy projekt.
W obszarze Zainstalowane szablony, kliknij Visual C#.
Na liście typów aplikacji, kliknij Biblioteka klas.
W polu Nazwa wpisz Bank a następnie kliknij OK.
[!UWAGA]
Jeśli nazwa "Bank" jest już używana, wybierz inną nazwę dla projektu.
Nowy projekt, o nazwie Bank, jest tworzony i wyświetlany w oknie Solution Explorer z plikiem Class1.cs, otwartym w edytorze kodu.
[!UWAGA]
Jeśli plik Class1.cs nie jest otwarty w edytorze kodu, kliknij dwukrotnie plik Class1.cs w oknie Solution Explorer, aby go otworzyć.
Skopiuj kod źródłowy z Przykładowy projekt dotyczący tworzenia testów jednostkowych.
Zamień oryginalną zawartość pliku Class1.cs na kod z Przykładowy projekt dotyczący tworzenia testów jednostkowych.
Zapisz plik jako BankAccount.cs
W menu Kompilacja kliknij Kompiluj rozwiązanie.
Masz teraz projekt o nazwie Bank.Zawiera on kod źródłowy do testowania i narzędzia, którymi można go testować.Obszar nazw dla projektu Bank, BankAccountNS, zawiera klasę publiczną BankAccount, której metody należy przetestować w następujących procedurach.
W szybkim starcie, koncentrujemy się na metodzie Debit. Metoda Debit jest wywoływana gdy pieniądze są wybierane z konta i zawiera następujący kod:
// method under test
public void Debit(double amount)
{
if(amount > m_balance)
{
throw new ArgumentOutOfRangeException("amount");
}
if (amount < 0)
{
throw new ArgumentOutOfRangeException("amount");
}
m_balance += amount;
}
Utwórz projekt testów jednostkowych
Warunek wstępny: wykonaj kroki procedury, Przygotuj Instruktaż.
Aby utworzyć projekt testów jednostkowych
W menu Plik wybierz Dodaj a następnie wybierz Nowy projekt ....
W oknie dialogowym Nowy projekt, rozwiń Zainstalowane, rozwiń Visual C#, a następnie wybierz Test.
Z listy szablonów wybierz Projekt testów jednostkowych.
W polu Nazwa wpisz BankTest, a następnie wybierz OK.
Projekt BankTests jest dodawany do rozwiązania Bank.
W projekcie BankTests dodaj odwołanie do rozwiązania Bank.
W oknie Solution Explorer, wybierz Odwołania w projekcie BankTests, a następnie wybierz polecenie Dodaj odwołanie... z menu kontekstowego.
W oknie dialogowym Reference Manager, rozwiń Rozwiązanie, a następnie zaznacz element Bank.
Utwórz klasę testową
Potrzebna jest klasa testowa do sprawdzania klasy BankAccount.Można użyć pliku UnitTest1.cs, wygenerowanego przez szablon projektu, ale powinno się nadać plikowi i klasie bardziej opisowe nazwy.Można to zrobić w jednym kroku, zmieniając nazwę pliku w oknie Solution Explorer.
Zmiana nazwy pliku klasy
W oknie Solution Explorer wybierz plik UnitTest1.cs, z projektu BankTests.Z menu kontekstowego wybierz polecenie Zmień nazwę, a następnie zmień nazwę pliku na BankAccountTests.cs.Wybierz Tak w oknie dialogowym z pytaniem, czy chcesz zmienić wszystkie odwołania w projekcie do elementu kodu '"UnitTest1".W tym kroku zmieniono nazwę klasy na BankAccountTest.
Plik BankAccountTests.cs zawiera teraz następujący kod:
// unit test code
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace BankTests
{
[TestClass]
public class BankAccountTests
{
[TestMethod]
public void TestMethod1()
{
}
}
}
Dodawanie instrukcji using, do testowanego projektu
Możemy również dodać instrukcję using do klasy, aby umożliwić wywołania testowanego projektu bez potrzeby użycia w pełni kwalifikowanych nazw.U góry pliku klasy należy dodać:
using BankAccountNS
Wymagania klasy testowej
Minimalne wymagania dla klasy testowej są następujące:
Atrybut [TestClass] jest wymagany we frameworku testów jednostkowych firmy Microsoft dla kodu zarządzanego, dla każdej klasy, która zawiera metody testów jednostkowych, które chcesz uruchomić w programie Test Explorer.
Każda metoda badania, który chcesz przetestować Explorer do uruchomienia musi mieć [TestMethod]atrybut.
Może mieć innych klas w projekcie badania jednostki, które nie mają [TestClass] atrybut, na które może mieć innych metod w klasach badania, które nie mają [TestMethod] atrybut.Te klasy i metody można użyć w swojej metody badań.
Tworzenie pierwszej metody testowej
W tej procedurze, zostaną napisane metody testów jednostkowych, aby zweryfikować zachowanie metody Debit klasy BankAccount.Metoda jest wymieniona powyżej.
Analizując testowaną metodę określamy, czy są przynajmniej trzy zachowania, które muszą być sprawdzone:
Metoda zgłasza wyjątek [ArgumentOutOfRangeException] jeśli kwota kredytu jest większa niż saldo.
Zgłasza również wyjątek ArgumentOutOfRangeException jeśli kwota kredytu jest mniejsza niż zero.
Jeżeli kontrole w 1.) i 2). są spełnione, metoda odejmuje kwotę od salda konta.
Pierwszy test sprawdza, czy prawidłowa kwota (taka, która jest mniejsza niż saldo konta i większa niż zero) powoduje wybranie poprawnej ilości z konta.
Aby utworzyć metodę testową
Dodaj instrukcję using BankAccountNS; do pliku BankAccountTests.cs.
Dodaj następującą metodę do klasy BankAccountTests:
// unit test code [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 dość prosta.Ustawiamy nowy obiekt BankAccount z saldem początkowym, a następnie wycofać prawidłową kwotę.Używamy metody frameworka testów jednostkowych firmy Microsoft dla kodu zarządzanego AreEqual aby sprawdzić, czy saldo końcowe jest takie, jakiego się spodziewaliśmy.
Wymagania metod testowych
Metoda testowa musi spełniać następujące wymagania:
Metoda musi posiadać atrybut [TestMethod].
Metoda musi zwracać void.
Metoda nie może mieć parametrów.
Skompiluj i uruchom test
Aby skompilować i uruchomić test
W menu Kompiluj wybierz polecenie Kompiluj rozwiązanie.
Jeśli nie ma żadnych błędów, pojawi się okno UnitTestExplorer z wymienionymi Debit_WithValidAmount_UpdatesBalance w grupie Nieuruchomione testy.Jeśli program Test Explorer nie jest wyświetlany po pomyślnej kompilacji, wybierz z menu polecenie Test, następnie wybierz polecenie Okna, a następnie wybierz Eksplorator testów.
Wybierz Uruchom wszystkie aby uruchomić test.Podczas gdy test jest uruchomiony, pasek stanu u góry okna, jest animowany.Na końcu przebiegu testowego, pasek zmienia kolor na zielony, jeśli wszystkie metody zakończyły się pomyślnie, lub czerwony jeśli którykolwiek z testów nie powiódł się.
W tym przypadku, test nie powiódł się.Metoda testowa jest przenoszona do Testy zakończone niepomyślnie.grupa.Wybierz metodę w programie Test Explorer, aby wyświetlić szczegóły u dołu okna.
Napraw kod i uruchom ponownie testy
Analizuj wyniki testu
Wynik testu zawiera komunikat, który opisuje błąd.Dla metody AreEquals wyświetlany jest komunikat, zawierający co było oczekiwane (parametr (Oczekiwano<XXX>) i co faktycznie zostało odebrane (parametr Rzeczywiste<YYY>).Oczekiwanym rezultatem było zmniejszenie salda początkowego, ale zamiast tego, saldo początkowe wzrosło o kwotę, która miała być wybrana z konta.
Ponowne testy kodu metody Debet pokazują, że testy jednostkowe zdołały znaleźć usterkę.Kwota wybrana z konta jest dodawana do salda konta kiedy powinna być odejmowana.
Należy poprawić usterkę
Aby poprawić błąd, wystarczy zamienić linię
m_balance += amount;
z
m_balance -= amount;
Uruchom ponownie test
W programie Test Explorer wybierz opcję Uruchom wszystkie, aby ponownie uruchomić test.Czerwony/zielony pasek zmienia kolor na zielony a test przenoszony jest do grupy Testy zakończone pomyślnie.
Użyj testów jednostkowych aby poprawić kod
W tej sekcji opisano, jak proces iteracyjny analizy, tworzenie testów jednostkowych i refaktoryzacja może pomóc w tworzeniu bardziej niezawodnego i skutecznego kodu.
Analizuj problemy
Po utworzeniu metody testowej, aby potwierdzić, że poprawna liczba jest prawidłowo odejmowana w metodzie Debit, można przejść do pozostałych przypadków w oryginalnej analizie:
Metoda zgłasza wyjątek ArgumentOutOfRangeException jeśli kwota kredytu jest większa niż saldo.
Zgłasza również wyjątek ArgumentOutOfRangeException jeśli kwota kredytu jest mniejsza niż zero.
Utwórz metody testowe
Pierwsza próba tworzenia metody testowej skierowanej do tych problemów wydaje się być obiecująca:
//unit test method
[TestMethod]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void Debit_WhenAmountIsLessThanZero_ShouldThrowArgumentOutOfRange()
{
// arrange
double beginningBalance = 11.99;
double debitAmount = -100.00;
BankAccount account = new BankAccount("Mr. Bryan Walton", beginningBalance);
// act
account.Debit(debitAmount);
// assert is handled by ExpectedException
}
Należy użyć atrybutu ExpectedExceptionAttribute do potwierdzenia, że zgłoszono poprawny wyjątek.Atrybut powoduje niepowodzenie testu, chyba że zgłaszany jest wyjątek ArgumentOutOfRangeException.Uruchamianie testu z zarówno dodatnimi jak i ujemnymi wartościami debitAmount i tymczasowe modyfikowanie testowanej metody, aby zgłosić generyczny wyjątek ApplicationException gdy kwota jest mniejsza niż zero, demonstruje, że test działa poprawnie.Aby przetestować przypadek, gdy wybierana kwota jest większa niż saldo, wszystko co potrzeba zrobić, to:
Utwórz nową metodę testową, o nazwie Debit_WhenAmountIsMoreThanBalance_ShouldThrowArgumentOutOfRange.
Skopiuj ciało metody z Debit_WhenAmountIsLessThanZero_ShouldThrowArgumentOutOfRange do nowej metody.
Ustaw debitAmount na liczbę większą niż saldo.
Uruchom testy
Uruchomione dwóch metod z różnymi wartościami debitAmount demonstruje, że testy odpowiednio obsługują pozostałe przypadki.Uruchamianie wszystkich trzech testów potwierdza, że wszystkie przypadki z oryginalnej analizy są poprawnie pokryte.
Kontynuuj analizę
Jednak, ostatnie dwie metody testowe są również nieco trudne.Nie możemy pewni, który warunek w testowanym kodzie zgłosi wyjątek, kiedy uruchomiony jest każdy test.Pomocny byłby jakiś sposób na rozróżnienie tych dwóch warunków.Po przemyśleniu problemu, staje się jasne, że znajomość tego, który warunek został naruszony, zwiększyłaby zaufanie do testów.Informacja ta, prawdopodobnie byłaby również pomocna dla mechanizmu produkcji, który obsługuje wyjątek, gdy jest generowany przez testowaną metodę.Generowanie większej ilości informacji, gdy metoda zgłasza wyjątek, mogło by pomóc wszystkim zainteresowanym, ale atrybut ExpectedException nie może dostarczyć tych informacji.
Patrząc ponownie na testowaną metodę widzimy, że obie instrukcje warunkowe używają konstruktora ArgumentOutOfRangeException, który pobiera nazwę argumentu jako parametr:
throw new ArgumentOutOfRangeException("amount");
Z wyszukiwania biblioteki MSDN możemy odkryć, że istnieje konstruktor, który zgłasza znacznie bogatsze informacje.ArgumentOutOfRangeException(String, Object, String) zawiera nazwę argumentu, wartość argumentu i wiadomość zdefiniowaną przez użytkownika.Możemy refaktoryzować testowaną metodę aby użyć tego konstruktora.Jeszcze lepiej byłoby użyć publicznie dostępnych typów elementów członkowskich do określenia błędów.
Refaktoryzuj testowany kod
Najpierw należy zdefiniować dwie stałe dla komunikatów o błędach w zakresie klasy:
// class under test
public const string DebitAmountExceedsBalanceMessage = "Debit amount exceeds balance";
public const string DebitAmountLessThanZeroMessage = "Debit amount less than zero";
Następnie należy zmodyfikować dwie instrukcje warunkowe w metodzie Debit:
// method under test
// ...
if (amount > m_balance)
{
throw new ArgumentOutOfRangeException("amount", amount, DebitAmountExceedsBalanceMessage);
}
if (amount < 0)
{
throw new ArgumentOutOfRangeException("amount", amount, DebitAmountLessThanZeroMessage);
}
// ...
Refaktoryzuj metody testowe
W metodzie testowej, należy najpierw usunąć atrybut ExpectedException.W jego miejsce, przechwytujemy zgłoszony wyjątek i sprawdzamy, czy został zgłoszony w poprawnej instrukcji warunkowej.Jednakże, trzeba teraz zdecydować pomiędzy dwiema opcjami sprawdzenia pozostałych warunków.Na przykład, w metodzie Debit_WhenAmountIsMoreThanBalance_ShouldThrowArgumentOutOfRange możemy wykonać jedną z następujących czynności:
Potwierdź, że właściwość ActualValue wyjątku (drugi parametr konstruktora ArgumentOutOfRangeException) jest większa niż saldo początkowe.Ta opcja wymaga testowania właściwości wyjątku ActualValue zamiast zmiennej testowanej metody beginningBalance, a następnie wymaga również sprawdzenia, czy wartość ActualValue jest większa niż zero.
Potwierdź, że wiadomość (trzeci parametr konstruktora) zawiera DebitAmountExceedsBalanceMessage zdefiniowaną w klasie BankAccount.
Metoda StringAssert.Contains frameworka testów jednostkowych firmy Microsoft, pozwala na sprawdzenie drugiej opcji bez obliczeń, które są wymagane przy pierwszej opcji.
Druga próba przeglądu Debit_WhenAmountIsMoreThanBalance_ShouldThrowArgumentOutOfRange może wyglądać jak:
[TestMethod]
public void Debit_WhenAmountIsGreaterThanBalance_ShouldThrowArgumentOutOfRange()
{
// arrange
double beginningBalance = 11.99;
double debitAmount = 20.0;
BankAccount account = new BankAccount("Mr. Bryan Walton", beginningBalance);\
// act
try
{
account.Debit(debitAmount);
}
catch (ArgumentOutOfRangeException e)
{
// assert
StringAssert.Contains(e.Message, BankAccount. DebitAmountExceedsBalanceMessage);
}
}
Ponów test, przepisz i analizuj ponownie
Podczas ponownego testowania metod z różnymi wartościami, można napotkać następujące fakty:
Jeżeli zostanie przechwycony poprawny błąd, przy użyciu wartości debitAmount, która jest większa niż saldo, asercja Contains przebiega pomyślnie, wyjątek jest ignorowany i w ten sposób testowana metoda kończy się powodzeniem.Jest to oczekiwane zachowanie.
Jeśli używamy wartości debitAmount, asercja nie powiedzie się, ponieważ zwracany jest niewłaściwy komunikat o błędzie.Asercja również kończy się niepowodzeniem, jeśli przedstawimy tymczasowy wyjątek ArgumentOutOfRange, w innym punkcie ścieżki kodu testowanej metody.Ten przypadek jest również poprawny.
Jeśli debitAmount wartość jest prawidłowa, (czyli mniej niż saldo, ale większa od zera, nie przechwycono, więc assert nigdy nie zostanie przechwycony.Testowana metoda kończy się powodzeniem.Ten przypadek nie jest dobry, ponieważ chcemy aby testowana metoda nie powiodła się jeśli nie jest zgłaszany żaden wyjątek.
Trzeci fakt jest usterką w testowanej metodzie.Aby spróbować rozwiązać problem, można dodać asercję Fail na koniec testowanej metody, aby obsłużyć przypadek, gdzie nie jest zgłaszany żaden wyjątek.
Jednak ponowne przetestowanie pokazuje, że teraz test kończy się niepowodzeniem, gdy przechwytywany jest poprawny wyjątek.Instrukcja catch resetuje wyjątek i metoda kontynuuje wykonywanie, kończąc się niepowodzeniem przy nowej asercji.Aby rozwiązać nowy problem, można dodać instrukcję return, po instrukcji StringAssert.Ponowne przetestowanie potwierdza, że problem został naprawiony.Ostateczna wersji Debit_WhenAmountIsMoreThanBalance_ShouldThrowArgumentOutOfRange wygląda następująco:
[TestMethod]
public void Debit_WhenAmountIsGreaterThanBalance_ShouldThrowArgumentOutOfRange()
{
// arrange
double beginningBalance = 11.99;
double debitAmount = 20.0;
BankAccount account = new BankAccount("Mr. Bryan Walton", beginningBalance);\
// act
try
{
account.Debit(debitAmount);
}
catch (ArgumentOutOfRangeException e)
{
// assert
StringAssert.Contains(e.Message, BankAccount. DebitAmountExceedsBalanceMessage);
return;
}
Assert.Fail("No exception was thrown.")
}
W tej sekcji końcowej, praca wykonana nad ulepszeniem kodu testu, doprowadziła do bardziej niezawodnych i wartościowych metod testowych.Ale co ważniejsze, dodatkowe analizy doprowadziły również do lepszego kodu, w testowanym projekcie.