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.

Tworzenie projektu do przetestowania

  1. Otwórz program Visual Studio.

  2. W oknie uruchamiania wybierz pozycję Utwórz nowy projekt.

  3. 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.

  4. 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 otwarte w edytorze kodu.

    Uwaga

    Jeśli plik Program.cs nie jest otwarty w edytorze, kliknij dwukrotnie plik Program.cs w Eksplorator rozwiązań, aby go otworzyć.

  5. Zastąp zawartość pliku 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);
            }
        }
    }
    
  6. Zmień nazwę pliku na BankAccount.cs, klikając prawym przyciskiem myszy i wybierając polecenie Zmień nazwę w Eksplorator rozwiązań.

  7. W menu Kompilacja kliknij pozycję Kompiluj rozwiązanie (lub naciśnij klawisze 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

  1. 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.

  2. 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.

  3. Nadaj projektowi nazwę BankTests i kliknij przycisk Dalej.

  4. Wybierz zalecaną strukturę docelową lub platformę .NET 8, a następnie wybierz pozycję Utwórz.

    Projekt BankTests jest dodawany do rozwiązania Bank .

  5. 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.

  6. W oknie dialogowym Menedżer odwołań rozwiń węzeł Projekty, wybierz pozycję Rozwiązanie, a następnie zaznacz element Bank.

  7. 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

  1. Aby zmienić nazwę pliku, w Eksplorator rozwiązań wybierz plik UnitTest1.cs w projekcie BankTests. W menu prawym przyciskiem myszy wybierz pozycję Zmień nazwę (lub naciśnij klawisz F2), a następnie zmień nazwę pliku na BankAccountTests.cs.

  2. Aby zmienić nazwę klasy, umieść kursor w UnitTest1 edytorze kodu, kliknij prawym przyciskiem myszy, a następnie wybierz polecenie Zmień nazwę (lub naciśnij klawisz F2). Wpisz ciąg BankAccountTests , a następnie naciśnij klawisz 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

  1. W menu Kompilacja wybierz pozycję Kompiluj rozwiązanie (lub naciśnij klawisze Ctrl + SHIFT + B).

  2. 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 klawisze Ctrl + E, T).

  3. Wybierz pozycję Uruchom wszystko , aby uruchomić test (lub naciśnij klawisze 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.

  4. 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 klawisze Ctrl + R, V). Czerwony/zielony pasek zmieni kolor na zielony, aby wskazać, że test zakończył się pomyślnie.

Test Explorer in Visual Studio 2019 showing passed test

Test Explorer in Visual Studio 2019 showing passed test

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:

  1. Utwórz nową metodę testową o nazwie Debit_WhenAmountIsMoreThanBalance_ShouldThrowArgumentOutOfRange.

  2. Skopiuj treść metody z Debit_WhenAmountIsLessThanZero_ShouldThrowArgumentOutOfRange do nowej metody.

  3. 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 StringAssertcatch 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.

Aby uzyskać informacje o sposobie uruchamiania testów z poziomu wiersza polecenia, zobacz OPCJE wiersza polecenia VSTest.Console.exe.