Ekstremalna przeróbka ASP.NET - Część 2: Testowanie Udostępnij na: Facebook

Autor: James Kovacs

Kod dostępny do pobrania z witryny MSDN Code Gallery

Witam z powrotem w drugim odcinku tej wieloczęściowej serii artykułów: Ekstremalna przeróbka ASP.NET. W części 1 zaczęliśmy naszą podróż w celu poprawienia istniejącej aplikacji WWW – ScrewTurn Wiki i przemienienia jej w bogatą aplikację internetową poprzez zastosowanie najnowszych technologii. Przedstawiłem podstawy programowania przekształcającego istniejące rozwiązanie – następujące cztery fundamentalne zasady inżynierii oprogramowania, które powinny być przyjęte przez każdy projekt niezależnie od metodologii.

  1. Kontrola wersji
  2. Śledzenie problemów
  3. Zautomatyzowane, zwarte budowania
  4. Zautomatyzowane testowanie

Część 1 skupiała się na pierwszych trzech spośród tych zasad. Ten artykuł, część 2, skupi się na ostatniej zasadzie - automatycznym testowaniu.

Chodzenie po linie, a testy

Stosowanie mocnego zestawu zautomatyzowanych testów zapewnia bazie kodu siatkę zabezpieczającą. Programiści mogą dość bezpiecznie modyfikować kod chroniony przez testy. Implementacje metod można zmieniać, hierarchie klas modyfikować, a kod poprawiać na mnóstwo sposobów. Zautomatyzowane testy można uruchamiać na kodzie w dowolnym momencie, aby weryfikować, że istniejąca funkcjonalność nie jest zepsuta. Istniejące zautomatyzowane testy działają w celu zapobiegania napływowi regresji do bazy kodu (regresja oznacza funkcjonalność, która wcześniej działała, ale teraz już nie działa).

Dlaczego zespół programistyczny chciałby modyfikować działający kod? Jest wiele powodów:

  • Trzeba naprawić błąd i chcemy go naprawić bez psucia innej funkcjonalności.
  • Myślimy o lepszej lub efektywniejszej metodzie implementacji danej funkcji.
  • Chcemy spłacić jakiś techniczny dług zaciągnięty przez zespół.

(Techniczny dług jest przenośnią utworzoną przez Warda Cunninghama do opisania stopniowego nagromadzenia problemów w bazie kodu na skutek szybkiego i niechlujnego programowania. Gdy dług techniczny się nagromadzi, zwiększa się trudność pracy z kodem. Można ograniczyć ten dług poprzez refaktoring kodu w celu wyeliminowania duplikacji, zwiększenia spójności, zmniejszenia sprzężenia, itd. Szanse bezpiecznego refaktoringu kodu rosną (ograniczając w ten sposób dług techniczny), jeśli mamy dobry zestaw zautomatyzowanych testów działający jak siatka zabezpieczająca.)

Tak jak siatka zabezpieczająca używana przez linoskoczków, zestaw zautomatyzowanych testów jest tylko tak dobry, jaki obszar obejmuje. Obecność siatki zabezpieczającej na niewiele się zda, jeśli wypadniemy poza zasięg tej siatki, lub jeśli dziury w tej siatce będą na tyle duże, aby przez nie wypaść. To samo odnosi się wobec zautomatyzowanych testów. Jeśli modyfikujemy kod, który nie jest testowany, to chodzimy po linie bez siatki zabezpieczającej.

Krajobraz testowania w świecie ASP.NET

Skupimy się na testowaniu zorientowanym na programistę, w tym na testach jednostkowych, integracyjnych i akceptacyjnych. Te terminy są często mylone i to co najczęściej jest nazywane „testami jednostkowymi”, to faktycznie są testy integracyjne. Programiści sądzą, że piszą testy jednostkowe, ponieważ korzystają z platformy testowania jednostkowego. To co my jako programiści nazywamy „platformami testowania jednostkowego”, to są w istocie po prostu „platformy testowania”, które mogą być używane do pisania rozmaitych typów testów.

Jeśli większość programistów w istocie tworzy testy integracyjne, to czym są testy jednostkowe? Testy jednostkowe zajmują się testowaniem pojedynczego składnika, którym w terminologii zorientowanej obiektowo jest klasa. W testach integracyjnych testujemy zestaw składników działających razem.

Istnieje duża różnorodność dostępnych platform testowych dla Microsoft .NET Framework. Najczęściej używanymi są NUnit, MbUnit, xUnit i MSTest. Wszystkie są wersjami dla .NET pierwotnej platformy testowej dla Javy – JUnit. W istocie pierwotną platformą testowania jednostkowego była platforma SUnit napisana przez Kenta Becka dla języka Smalltalk, ale nie była szeroko rozpowszechniona. Beck oraz Erich Gamma zostali następnie współautorami platformy JUnit, która zyskała szerokie uznanie.

ScrewTurn Wiki wykorzystuje już NUnit do testów jednostkowych i integracyjnych, a my będziemy również nadal korzystać z NUnit w tej serii. Testy jednostkowe i integracyjne odnoszą się do logiki biznesowej, dostępu do danych, przepływu zadań i podobnych. Nie wchodzą w interakcję z warstwą HTML i JavaScript strony WWW. Testy te zapewniają nam siatkę zabezpieczającą przy pracy z podstawowym kodem aplikacji. Zamierzamy szczegółowo zbadać testy jednostkowe i integracyjne ScrewTurn Wiki w przyszłym artykule, gdy będziemy mówić o refaktoringu podstawowego kodu aplikacji. W kilku następnych artykułach będziemy aktualizować i poprawiać HTML, CSS i JavaScript dla ScrewTurn Wiki. Tutaj też potrzebujemy siatki zabezpieczającej, więc kierujemy swoją uwagę w tym artykule na zautomatyzowane testy akceptacyjne wokół interfejsu użytkownika.

Zautomatyzowane testy akceptacyjne

Testowanie akceptacyjne jest czarną skrzynką testującą system w celu zapewnienia, że odpowiada potrzebom użytkownika końcowego. Testy akceptacyjne często charakteryzują się długimi, rozbudowanymi dokumentami Worda z ręcznymi instrukcjami, które są wykonywane i weryfikowane przez departament zapewniania jakości lub użytkowników końcowych. Oto prosty, ręczny test akceptacyjny:

Zweryfikować wymaganie poświadczeń administracyjnych przez portal administracyjny

  1. Przejść do strony głównej pod adresem https://server/Default.aspx.
  2. Jeśli użytkownik jest aktualnie zalogowany, kliknąć Logout.
  3. Zweryfikować, że mamy dostęp do witryny jako gość („Witaj gościu” powinno wyświetlać się w prawym górnym rogu).
  4. Przejść do https://server/Admin.aspx.
  5. Zweryfikować, że zostajemy przekierowani do https://server/Login.aspx i poproszeni o poświadczenia.

Typowy system będzie miał dziesiątki lub setki takich testów. Testowanie ręczne jest powtarzalne, monotonne, czasochłonne i podatne na błędy. Natychmiastowe pytanie, które powinno przyjść do głowy, to „Jak trudno jest automatyzować te testy akceptacyjne?” Jak się okazuje jest to o wiele prostsze niż można by sądzić.

Wprowadzenie do WatiN

WatiN jest platformą testowania aplikacji WWW napisaną w C#. Jest to skrót pełnej nazwy Web Application Testing in .NET zainspirowanej przez WatiR - Web Application Testing in Ruby. Aby skorzystać z WatiN, wystarczy dodać odwołanie do WatiN.Core w naszym projekcie testowym i zacząć pisać kod testów. Integruje się ze wszystkimi ważniejszymi platformami testowania dla .NET. Oto fragment kodu NUnit wykorzystującego WatiN:

[Test]
public void CanBrowseToMicrosoft() {
    using(var browser = new IE()) {
        browser.GoTo("https://www.microsoft.com/");
        Assert.AreEqual("Microsoft Corporation", browser.Title);
    }
}

W powyższym kodzie IE jest klasą zdefiniowaną w WatiN.Core. Gdy tworzone jest wystąpienie IE, to uruchamiane jest faktyczne wystąpienie Internet Explorera. WatiN automatyzuje przeglądarkę nakazując jej przejście do określonego adresu URL i pobranie tytułu docelowej strony. WatiN może też klikać przyciski i łącza, wypełniać pola formularzy, ustawiać kursor myszy nad elementami i niemal wszystko inne, co może zrobić prawdziwy użytkownik. Ponieważ WatiN automatyzuje prawdziwą przeglądarkę, to uzyskujemy prawdziwą obsługę JavaScript i Asynchronous JavaScript + XML (AJAX). Przy pomocy WatiN możemy automatyzować skomplikowane interakcje z użytkownikiem, przebiegające przez wiele stron takie, jak dodawanie lub edytowanie strony w wiki, kupowanie towaru na witrynie e-handlowej (włącznie z wieloetapowym zatwierdzaniem zamówienia) lub wypełnianie wielostronicowej ankiety na witrynie firmy badającej opinię publiczną. Zamiast pisać odizolowane proste testy, możemy w istocie automatyzować związane z danym biznesem przepływy zadań. Ponieważ uruchamiamy testy akceptacyjne poprzez platformę testowania, to możemy tworzyć czystą bazę danych i/lub witrynę z domyślną zawartością przed każdym uruchomieniem testu, co maksymalizuje możliwość tworzenia powtarzalnego zestawu testów akceptacyjnych, które można uruchamiać na życzenie bez interakcji z użytkownikiem.

Oprócz automatyzacji Internet Explorera, WatiN 2.0 (będący aktualnie w wersji beta) może automatyzować przeglądarkę Mozilla Firefox v2/v3 i ma bardzo wstępną obsługę przeglądarki Google Chrome. W końcu będziemy w stanie wykonywać pewne testy na wielu przeglądarkach przy pomocy WatiN, chociaż nie na wielu platformach, ponieważ WatiN bezpośrednio automatyzuje przeglądarkę, która musi działać w systemie Windows. Skupimy się na Internet Explorerze, ponieważ obsługa WatiN dla tej przeglądarki jest najbardziej dojrzała.

Konfigurowanie WatiN

Czy nie byłoby cudownie, gdyby oprogramowanie po prostu za każdym razem działało od razu? WatiN jest nieco trudniej zmusić do współpracy. Aby uruchomić WatiN w swoim systemie, mamy do pokonania dwie przeszkody. WatiN komunikuje się z przeglądarką Internet Explorer poprzez interfejs API automatyzacji COM. WatiN może ukryć przed nami prawie wszystkie szczegóły związane z COM za wyjątkiem jednej rzeczy: modelu wątków. Interfejs Internet Explorer COM automation API zakłada, że działamy w modelu jedno-wątkowym (STA). W języku COM oznacza to, że tylko jeden wątek może komunikować się z danym składnikiem. Niektóre moduły uruchamiające testy wykorzystują model wielowątkowy (MTA), co oznacza, że dowolny wątek może mieć dostęp do składnika. Na przykład moduły uruchamiające testy dla NUnit i MbUnit wykorzystują domyślnie MTA. Moduły uruchamiające testy dla xUnit, MSTest, TestDriven.NET i JetBrains ReSharper wykorzystują STA. Warto zauważyć, że zastosowanie wątku STA lub MTA do uruchamiania testów jest zależne od modułu uruchamiającego testy, a nie od platformy testowej. TestDriven.NET wywołując testy NUnit będzie je uruchamiać w wątku STA, ale NUnit GUI wywołując testy NUnit będzie je uruchamiać w wątku MTA. Aby jeszcze bardziej skomplikować sprawy, MbUnit zmienia domyślne zachowanie modułu uruchamiającego testy, więc TestDriven.NET wywołując testy MbUnit będzie je uruchamiać w wątku MTA! To długi sposób na powiedzenie, że trzeba uruchamiać testy WatiN w wątku STA i lepiej samemu się o to zatroszczyć, aby WatiN mógł się z powodzeniem komunikować z Internet Explorerem. W przypadku NUnit dodajemy plik konfiguracyjny aplikacji o nazwie <NazwaPodzespołuTestowego>.dll.config (którym w naszym przypadku jest WebApplication-Test.dll.config) do naszego projektu testowego, jak pokazano na Rysunku 1.

Rysunek 1: WebApplication-Test.dll.config.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <sectionGroup name="NUnit">
      <section name="TestRunner" 
               type="System.Configuration.NameValueSectionHandler"/>
    </sectionGroup>
  </configSections>

  <NUnit>
    <TestRunner>
      <add key="ApartmentState" value="STA" />
    </TestRunner>
  </NUnit>

</configuration>

We właściwościach pliku WebApplication-Tests.dll.config pokazanych na Rysunku 2 należy zmienić opcję „Copy to Output Directory” na „Copy if newer”.


Rysunek 2: Widok właściwości pliku.

Jeśli korzystamy z innej platformy testowej, to w witrynie WatiN możemy znaleźć informacje, jak prawidłowo skonfigurować model wątków.

Jeszcze jedna zmiana i będziemy w stanie wykonywać swoje testy WatiN. Internet Explorer 7 i 8 w systemie Windows Vista lub późniejszym działa domyślnie w trybie chronionym. Tryb chroniony uniemożliwia przeglądarce Internet Explorer (i tym samym wszelkiej zawartości WWW uruchamianej wewnątrz) modyfikowanie naszego systemu. Przeszkadza też w komunikacji pomiędzy Internet Explorerem a WatiN. Najprostszym sposobem wyłączenia trybu chronionego dla danej witryny jest dodanie jej do listy zaufanych witryn. Jak pokazano na Rysunku 3, dodałem https://www.microsoft.com do listy zaufanych witryn, aby uruchamiać nasz prosty test WatiN, tak samo zrobiłem w przypadku https://localhost, ponieważ będziemy wykorzystywać lokalny serwer WWW do obsługi naszej witryny testowej.


Rysunek 3: Ekran Zaufane witryny.

Więcej szczegółowych informacji dotyczących WatiN i trybu chronionego Internet Explorera można znaleźć we wpisie na moim blogu zatytułowanym „Running WatiN Tests on Vista”. Więcej informacji na temat trybu chronionego Internet Explorera można uzyskać w bibliotece MSDN pod hasłem „Understanding and Working in Protected Mode Internet Explorer”.

Gdy model wątkowy COM modułu uruchamiania testów jest ustawiony na STA, a tryb chroniony Internet Explorera dla https://www.microsoft.com jest wyłączony, możemy uruchomić prosty test WatiN pokazany powyżej, który przechodzi do strony głównej firmy Microsoft i sprawdza tytuł tej strony.

Korzystanie z WatiN do testowania lokalnej witryny WWW

Teraz gdy udało nam się uruchomić prosty test WatiN dla zdalnej witryny WWW, skierujmy swoją uwagę na korzystanie z WatiN do testowania ScrewTurn Wiki. Nie będziemy chcieli wdrażać ScrewTurn Wiki na serwerze za każdym razem, gdy będziemy chcieli uruchomić swój zestaw testów akceptacyjnych przy pomocy WatiN. Zamiast tego wykorzystamy ASP.NET Development Server (WebDev.WebServer.exe), lepiej znany pod swoją nazwą kodową „Cassini”. Serwer ten jest instalowany z .NET Framework 2.0 i nowszymi wersjami w folderze C:\Windows\Microsoft.NET\Framework\v2.0.50727\. Inną opcją mogłoby być wykorzystanie lokalnego serwera IIS, ale ASP.NET Development Server ma tę miłą funkcję, że może go uruchamiać i/lub zatrzymywać normalny użytkownik.

Chcemy uruchamiać ASP.NET Development Server zanim uruchomione zostaną jakiekolwiek testy WatiN i zatrzymywać go po zakończeniu testowania. Większość platform testowania ma metodę do określania kodu, który ma być uruchamiany przed i po testach. W NUnit 2.4.8 używamy atrybutu SetUpFixture dla klasy, aby oznaczyć ją jako zawierającą kod inicjujący/zamykający dla wszystkich testów w tej samej przestrzeni nazw, jak pokazano na Rysunku 4.

Rysunek 4: Atrybut SetUpFixture w NUnit.

using System.Configuration;
using NUnit.Framework;

namespace ScrewTurn.Wiki.Tests {
    [SetUpFixture]
    public class WebServerRunner {
        private WebServer webServer;

        [SetUp]
        public void StartWebServer() {
            var absolutePathToWebsite = ConfigurationManager.AppSettings["absolutePathToWebsite"];
            webServer = new WebServer(absolutePathToWebsite, 9999);
            webServer.Start();
        }

        [TearDown]
        public void StopWebServer() {
            webServer.Stop();
            webServer = null;
        }
    }
}

Klasa WebServer jest klasą pomocniczą, która obsługuje ASP.NET Development Server. Wymaga fizycznej ścieżki do skompilowanej witryny WWW oraz ustalonego portu. Potrzebny jest nam stały port taki jak 9999, abyśmy mogli kierować WatiN do przeglądania witryny https://localhost:9999 w naszych testach.

ASP.NET Development Server w systemie Windows Vista i późniejszych

ASP.NET Development Server nasłuchuje tylko żądań w protokole IPv4, ale nie IPv6. Jeśli korzystamy z systemu Windows Vista lub Windows Server 2008, to żądania dla nazwy „localhost” będą domyślnie tłumaczone na adres IPv6 – ::1, a nie na adres IPv4 – 127.0.0.1 i testy WatiN się nie udadzą. Aby rozwiązać ten problem, trzeba zmienić adres localhost dla IPv6 w komentarz w naszym pliku C:\Windows\System32\drivers\etc\hosts. Chodzi o wiersz zawierający "::1 localhost".

Metoda Start klasy WebServer formatuje argumenty wiersza polecenia dla WebDev.WebServer.exe i uruchamia ten serwer wykorzystując obiekt System.Diagnostics.Process:

public void Start() {

    webServerProcess = new Process();

    const string webDevServerPath = 
    @"c:\Windows\Microsoft.NET\Framework\v2.0.50727\WebDev.WebServer.exe"";

    string arguments = string.Format("/port:{0} /path:\"{1}\" /vpath:{2}", 
    port, physicalPath, virtualDirectory);

    webServerProcess.StartInfo = new ProcessStartInfo(webDevServerPath, arguments);

    webServerProcess.Start();

}

Na końcu działania testów, metoda Stop kończy proces serwera Cassini:

public void Stop() {
    if(webServerProcess == null) {
        throw new InvalidOperationException("Start() must be called before Stop()");
    }
    webServerProcess.Kill();
}

UWAGA: Możemy otrzymać dziwny komunikat o błędzie z ASP.NET Development Server informujący, że nie istnieje katalog '<PATH> /vpath:', jak widać na Rysunku 5. Jest to błąd w programie ASP.NET Development Server, który powoduje niepowodzenie, jeśli ścieżka fizyczna kończy się znakiem \. Aby obejść ten błąd, wystarczy po prostu usunąć końcowy znak ukośnika w naszym pliku konfiguracyjnym lub kodować defensywnie stosując metodę physicalPath.TrimEnd(\\) dla wszelkich ścieżek fizycznych przeznaczonych dla programu ASP.NET Development Server, jak to zrobiłem w konstruktorze dla ScrewTurn.Wiki.Tests.WebServer.


Rysunek 5: ASP.NET Development Server wyświetlający komunikat błędu.

Testy dymne

Naszym celem przy pisaniu testów akceptacyjnych jest zapewnienie, że funkcjonalność dla użytkownika końcowego będzie cały czas działać zgodnie z oczekiwaniami w miarę dodawania przez nas funkcji lub refaktoringu istniejącego kodu. Zanim przejdziemy do skomplikowanych przepływów zadań, napiszemy kilka prostych testów dymnych korzystając z WatiN, aby zapewnić, że podstawowa funkcjonalność ScrewTurn Wiki działa (test dymny jest wstępnym testem mającym pokazać proste i oczywiste błędy takie jak błąd w pliku web.config sprawiające, że witryna nie jest dostępna). Nasze testy dymne obejmują działania związane z:

  • Przeglądaniem strony głównej
  • Logowaniem się do witryny
  • Przekierowaniem na stronę logowania ze stron administracyjnych
  • Przekierowaniem na stronę informacyjną w przypadku próby dostępu do nieistniejącej strony.

Trzeba zwrócić uwagę, że nie musimy o wszystkim pomyśleć natychmiast, ale możemy stopniowo dodawać testy w miarę budowania naszego zestawu testów.

Zaczniemy od testowania, czy użytkownik może zalogować się na witrynie jako administrator (domyślnie kontem administracyjnym ScrewTurn Wiki jest admin/password). Skrypt WatiN powinien być dość zrozumiały. Wyszukujemy elementy strony takie jak łącze do logowania, a następnie wykonujemy działania takie jak kliknięcie łącza do logowania poprzez metodę Click, jak pokazano na Rysunku 6.

Rysunek 6: Skrypt testu CanLogIntoSite w WatiN.

[Test]
public void CanLogIntoSite() {
    using(var browser = new IE()) {
        browser.GoTo("https://localhost:9999");
        browser.Link(Find.ByTitle("Login")).Click();
        browser.TextField(Find.ByTitle("Type here your Username")).TypeText("admin");
        browser.TextField(Find.ByTitle("Type here your Password")).TypeText("password");
        browser.Button(Find.ByValue("Login")).Click();
        var username = browser.Link(Find.ByTitle("Go to your Profile")).Text;
        Assert.That(username == "admin");
    }
}

Jedyną nieco skomplikowaną częścią może być to, jak znaleźć elementy strony (np. Skąd wiedziałem, żeby znaleźć pole tekstowe dla nazwy użytkownika szukając według jego tytułu „Type here your Username”?) Potrzebna jest nam wybrana przeglądarka oraz jedno lub kilka narzędzi, na przykład Internet Explorer i Internet Explorer Developer Toolbar lub Firefox i Firebug/Web Developer Toolbar.

Wyszukiwanie poprzez wybieranie tytułu elementu nie jest zalecane, ponieważ istnieje prawdopodobieństwo, że tytuł się zmieni. Na przykład ten tekst powinien w istocie brzmieć "Type your username here". Jeśli jednak poprawimy błąd gramatyczny, to zepsujemy test. Chciałem utworzyć testy dymne przed zmodyfikowaniem witryny, dlatego właśnie zostawiłem ten tekst w dotychczasowym brzmieniu. Lepszą opcją przy identyfikowaniu elementów strony jest korzystanie z identyfikatorów (możemy znajdować elementy według identyfikatorów korzystając z Find.ById("id") zamiast Find.ByTitle("title"). Klasa Find zawiera szeroką gamę różnych metod statycznych upraszczających lokalizowanie elementów strony). Chociaż więc WatiN może automatyzować dowolną witrynę, to możemy sprawić, aby nasze testy były solidniejsze w obliczu zmian (i tłumaczeń na inne języki) zapewniając identyfikatory dla interesujących nas elementów.

Eliminowanie powtórzeń z testów

Zauważymy wiele możliwych powtórzeń w teście CanLogIntoSite (Rysunek 5).

  • Każdy test WatiN będzie miał adres URL zaczynający się od https://localhost:9999.
  • Wiele testów będzie wymagało zalogowania się od użytkownika
  • Wiele testów może chcieć zweryfikować aktualnie zalogowanego użytkownika.

Jak możemy sobie wyobrazić, podobne powtórzenia będą pojawiać się w miarę pisania kolejnych testów. Zastosujmy zasadę „Nie powtarzaj się” do naszego testu WatiN.

[Test]
public void CanLogIntoSite() {
    using(var browser = new IE()) {
        browser.GoTo(PageUrl.Default);
        browser.LoginAsAdmin();
        Assert.That(browser.IsLoggedInAs("admin"));
    }
}

Utworzyłem klasę PageUrl, która jest po prostu listą identyfikatorów Uri dla stron w naszej witrynie. Utworzyłem również kilka metod rozszerzających w klasie Browser, aby przedstawić w skryptach typowe działania, jak pokazuje Rysunek 7.

Rysunek 7: Metody rozszerzające w klasie Browser z WatiN.

public static void LoginAsAdmin(this Browser browser) {
    if(browser.IsLoggedInAs("admin")) {
        return;
    }
    browser.Link(Find.ByTitle("Login")).Click();
    browser.TextField(Find.ByTitle("Type here your Username")).TypeText("admin");
    browser.TextField(Find.ByTitle("Type here your Password")).TypeText("password");
    browser.Button(Find.ByValue("Login")).Click();
}
public static bool IsLoggedInAs(this Browser browser, string expectedUsername) {
    var username = browser.Link
(Find.ByTitle(title => title == "Go to your Profile" || 
title == "Select your language")).Text;
    return username == expectedUsername;
}

Nie tylko zachęcam do korzystania ze swoich metod rozszerzających, ale mogę lepiej upewnić się, co robi mój test CanLogIntoSite korzystając ze zwięzłych, sensownych nazw dla metod pomocniczych. Zmniejszyłem też koszty utrzymania, ponieważ, kiedy w końcu dodam identyfikatory dla pól tekstowych dla nazwy użytkownika i hasła oraz poprawię gramatykę, to zminimalizowana zostanie liczba zmian potrzebnych do naprawienia testów.

Testy akceptacyjne

Testy akceptacyjne wykorzystujące WatiN są po prostu dłuższymi wersjami testów dymnych. Rysunek 8pokazuje test akceptacyjny sprawdzający, że możemy utworzyć nową stronę w naszym wiki.

Rysunek 8: Test akceptacyjny.

[Test]
public void CanCreateNewWikiPage() {
    string pageName = string.Format("TestPageName-{0}", Guid.NewGuid());
    const string pageTitle = "TestPageTitle";
    using(var browser = new IE()) {
        browser.GoTo(PageUrl.Default);
        browser.LoginAsAdmin();
        browser.CreateWikiPage(pageName, pageTitle);
        Assert.That(browser.WikiPageTitle(), Is.EqualTo(pageTitle));
        Assert.That(browser.Url, Text.Contains(pageName));
    }
}

Utworzyłem metodę rozszerzającą BrowserExtensions.CreateWikiPage, aby zawrzeć w niej logikę tworzenia nowej strony.

public static void CreateWikiPage(this Browser browser, string pageName, string pageTitle) {
    browser.Link(Find.ByTitle("Create a new Page")).Click();
    browser.TextField(Find.ByTitle("Type here the name of the page")).TypeText(pageName);
    browser.TextField(Find.ByTitle("Type here the title of the page")).TypeText(pageTitle);
    browser.Button(Find.ByValue("Save")).Click();
}

Inna metoda rozszerzająca upraszcza znajdowanie tytułu strony wiki; nie chodzi tu o zawartość znacznika <title/>, ale zawartość znacznika <h1 class="pagetitle"/>.

public static string WikiPageTitle(this Browser browser) {
    return browser.ElementWithTag("h1", Find.ByClass("pagetitle")).Text.Trim();
}

Gdy napiszemy więcej testów akceptacyjnych, to klasa BrowserExtensions może urosnąć do nieporęcznych rozmiarów. Jeśli tak się stanie, mogę łatwo przemodelować kod na dwie klasy BrowserLookupExtensions (do znajdowania informacji na stronie) i BrowserScriptingExtensions (do manipulowania stroną). Mógłbym też podzielić metody rozszerzające na publiczne i administracyjne, według określonej funkcji lub stosując jakąś inną metodę, aby kod był bardziej poręczny.

Podsumowanie

Jak zobaczyliśmy, zautomatyzowany zestaw testów zapewnia zespołowi programistycznemu siatkę zabezpieczającą. Testy dymne sprawdzają podstawową funkcjonalność witryny (na przykład to, że witryna jest dostępna), natomiast bardziej szczegółowe testy akceptacyjne sprawdzają funkcjonalność związaną z biznesem (na przykład to, że odwiedzający mogą aktualizować strony wiki). Zestaw zautomatyzowanych testów pomaga zapewnić, że kod roboczy nadal działa zgodnie z oczekiwaniami. Zapewnia to zespołowi większą wolność i pewność siebie przy poprawianiu bazy kodu i eliminowaniu długu technicznego. W części 3 zwrócimy naszą uwagę na poprawianie XHTML/CSS i zastosowanie naszych testów dymnych i akceptacyjnych w celu zapewnienia, że nie psujemy interfejsu użytkownika podczas naszych modyfikacji.

James Kovacs jest niezależnym architektem, programistą, szkoleniowcem i złotą rączką, mieszkającym w Calgary w stanie Alberta, specjalizującym się w programowaniu zwinnym przy użyciu .NET Framework. Ma tytuł Microsoft MVP for Solutions Architecture i uzyskał stopień magistra na Harvard University. Można się z nim skontaktować pod adresem jkovacs@post.harvard.edu lub www.jameskovacs.com.