Pokazywanie postów oznaczonych etykietą TFS. Pokaż wszystkie posty
Pokazywanie postów oznaczonych etykietą TFS. Pokaż wszystkie posty

niedziela, 4 grudnia 2011

Scrum i Team Foundation Server cz.6 - Proces TDD

W kilku ostatnich wpisach przedstawiłem czym jest metodyka Scrum, po co i jak ją stosować przy użyciu Team Foundation Server. W teorii moglibyśmy już rozpocząć projekt i z powodzeniem go prowadzić. Możliwe, że więcej wiedzy nie było by Wam szybko potrzebne, ale podejrzewam, że całkiem szybko zaczęły się pojawiać pytania o zarządzanie i przepływ zadań, np.:
-  Który rodzaj zadania jest  z czym powiązany?
- Czy jak dodamy buga to mamy dodać do niego też taska?
- do czego służy test case a do czego impediment?
- itd. Itp.
Takie pytania są nieuchronne, każdy Scrum Master będzie musiał je wysłuchiwać po 100 razy. Można jednak ich częstotliwość zmniejszyć poprzez spisanie dokumentu, zawierającego przykładowy przepływ pomiędzy zadaniami oraz zasady tworzenia dokumentów. Dzięki niemu będziemy mogli w krytycznej sytuacji bez większych wyrzutów sumienia odpowiedzieć „zajrzyj sobie do dokumentacji”.

W Scrum centralnym elementem opisu biznesowego przypadku jest Historia Użytkownika (User Story) zwana również „elementem rejestru produktu” (Product Backlog Item – PBI). Opisuje ona wartość biznesową, która ma zostać dodana do projektu np. ”Jako użytkownik gry One Card Master chcę, zobaczyć informację o aktualnej liście uczestników gry, abym wiedział z kim gram”.
Schemat można było by opisać (tak jak przedstawiono we wpisie) przez:
JAKO <Osoba, rola>
CHCĘ <Funkcjonalność, czynność>
ABY <Uzasadnienie biznesowe>
W teorii pisanie historii użytkownika wydaje się sprawą zupełnie prostą, ale w praktyce okazuje się, że jest zupełnie inaczej. Same zasady tworzenia historii użytkownika są materiałem na osobny wpis.
No ale wracając do tematu procesu. Tak jak wspomniałem we wpisie Scrum jest mocno powiązany z metodyką TDD. Podążając z jej zasadami, będzie nam dużo łatwiej prowadzić projekt, oraz zarządzać przepływem. We wspomnianym wpisie powiedziałem, że każdy PBI powinien mieć wyraźnie i dokładnie opisane kryteria akceptacji – czyli warunki, które muszą zostać spełnione, żeby zadanie zostało zaakceptowane.

Same historie użytkownika opisują jedynie ogólne aspekty realizacji biznesowego problemu. Aby kompleksowo zamodelować proces wytwarzania funkcjonalności konieczne jest dodanie do nich konkretnych elementów tj:
Źródło: Crispin Parker's Blog

- Task – relacja „Implemented-By” – czyli opisuje wszystko to co jest konieczne do tego, żeby od strony programistycznej zaimplementować historię użytkownika
- Acceptance Test – relacja „Tested-By” – opisuje testy akceptacyjne, czyli to w jaki sposób historia użytkownika będzie testowana
- Bug Report – relacja „Failed – By” – opisuje co poszło nie tak przy testach akceptacyjnych
- Impediment – relacja „Impeded-By” – przeszkoda, Opisuje problemy, które wystąpiły przy implementacji historii użytkownika, może to być np. sytuacja, że przy tworzeniu algorytmu nie wzięliśmy pod uwagę jakiegoś aspektu, którego zaimplementowanie wymaga dodatkowej analizy

Co z sytuacjami gdy odnajdziemy inne błędy, które nie zostały ujęte w testy akceptacji? Dorzucamy do rejestru produktu (ewentualnie rejestru spritu jeżeli jest on kluczowy dla jego oddania) element typu Bug.  Ma on identyczną strukturę zależności jak historia użytkownika. Powinien również posiadać zadanie, opis testów, które będą dokonywane przy weryfikacji tego buga, może zawierać Impediment oraz raport o błędzie, który mówi o tym, że rozwiązanie błędu nie zostało zaakceptowane.

Paczkę koniecznej wiedzy teoretycznej już uzyskaliśmy, możemy teraz przejść do przykładu. Będzie on pokazany na Team Foundation Server 11 DP oraz Visual Studio 11 DP. Załóżmy, że tworzymy znaną z innych wpisów grę One Card Master. Mamy już zaimplementowaną część kodu odpowiadającego za zarządzanie graczami. Dostępne już są klasy:

- informacje o graczu:

public class Player
{
    public string Name { get; set; }
}

- zarządzajanie graczami, pozwalające na ich dodawanie

public interface IPlayersManager
{
    IList<Player> GetPlayers();

    void AddPlayer(Player player);
}

public class PlayersManager : IPlayersManager
{
    private readonly IList<Player> _playersList = new Listt<Player>();
        
    public void AddPlayer(Player player)
    {
        _playersList.Add(player);
    }

    public IList<Player> GetPlayers()
    {
        return _playersList;
    }
}

- dostępne są również klasy pozwalające na drukowanie informacji na ekranie

public interface IPrinter
{
    void Print(string text);
}

public class ConsolePrinter : IPrinter
{
    public void Print(string text)
    {
        Console.WriteLine(text);
    }
}

Klient zażyczył sobie, że musimy dodać funkcjonalność wyświetlania aktualnej listę graczy. 
Załóżmy, że znajdujemy się na etapie planowania sprintu. Pierwszym krokiem, który powinniśmy zrobić jest dodanie nowej historii użytkownika. Dokonujemy tego poprzez kliknięcie menu jak na poniższym obrazku.


Historia użytkownika

Ukaże nam się okno definicji historii użytkownika:


Nazywamy naszą historię użytkownika "Wyświetlanie aktualnej listy graczy" podajemy jego opis:

”Jako użytkownik gry One Card Master chcę, zobaczyć informację o aktualnej liście uczestników gry, abym wiedział z kim gram”

Podajemy kryteria akceptacji:

1. Po dołączeniu nowego gracza system powinien wyświetlić zaktualizowaną listę użytkowników.
2. W obecnej wersji powinien wyświetlać informacje na konsoli.
3. Informacja o użytkowniku powinna być zapisana w formacie "{Lp}. Nazwa: {Nazwa}".

Przypadek testowy

Zapisujemy historię użytkownika, ale nie zamykamy okna tylko przechodzimy do zakładki "Test cases" i naciskamy przycisk "New".


Dodajemy w ten sposób test akceptacji. Nazywamy go "Test wyświetlania aktualnej listy graczy" i naciskamy "OK". Otworzy się nam widok przypadku testowego. Zapisujemy go. Dostaniemy teraz pełen dostęp do opcji tworzenia testu historii użytkownika.
Jak łatwo zauważyć cały czas postępujemy tutaj zgodnie z metodyką TDD:
- najpierw opisaliśmy co chcemy (historia użytkownika),
- następnie co musi być spełnione, żeby uznać funkcjonalność za poprawnie działającą (kryteria akceptacji),
- teraz napiszemy jak będziemy to testować.


Naciskamy przycisk "Edit with Microsoft Test Manager". Po tej akcji przejdziemy do zewnętrznego programu przygotowanego przez Microsoft specjalnie do planowania, zarządzania oraz przeprowadzania testów (postaram się go przedstawić w osobnym wpisie). Nie wgłębiając się specjalnie w szczegóły - posłuży on nam do zdefiniowania kroków naszego testu.


Dodajemy trzy kroki (poprzez uzupełnienie odpowiednich pól w tabelce "Steps"):

"1. Dodajemy gracza "Jan Kowalski" poprzez metodę AddPlayer klasy PlayersManager - Wyświetlona informacja: "01. Nazwa: Jan Kowalski"
2. Dodajemy gracza "Krzysztof Krawczyk" poprzez metodę AddPlayer klasy PlayersManager - Wyświetlone informacje: "01. Nazwa: Jan Kowalski", "02. Nazwa: Krzysztof Krawczyk"
3. Dodajemy gracza "Eustachy Janicki" poprzez metodę AddPlayer klasy PlayersManager - Wyświetlone informacje: "01. Nazwa: Jan Kowalski", "02. Nazwa: Krzysztof Krawczyk", "03. Eustachy Janicki" "

Przechodzimy do zakładki Summary i przeklejamy tam tekst z kryteriów akceptacji, zapisujemy zmiany i zamykamy Test Managera. Po odświeżeniu widoku Test Case'a powinny pojawić się na nim wpisane przez nas dane.

Zadanie

Wracamy ponownie do naszej historii użytkownika. Dodamy teraz zadanie developerskie poprzez przejście do zakładki Tasks i naciśnięcie przycisku "New". Nazywamy go "Implementacja wyświetlania aktualnej listy graczy".



Wypełniamy pola zgodnie z powyższym obrazkiem (opis jako: "Należy stworzyć taki mechanizm, który po dodaniu gracza poprzez metodę AddPlayer z klasy PlayerManager automatycznie wyświetli na ekranie aktualną listę graczy.") i przypisujemy go do osoby, która będzie go realizowała (np. do siebie) poprzez "Assigned To".
Gdy zaczniemy realizację zadania ustawiamy jego status (State) na "In Progress", oraz status PBI na "Approved".

Przeszkoda

Załóżmy, że okazało się, że nie wiemy jak coś zrobić. Specyfikacja jest niedokładna, mamy problem z wymyśleniem odpowiedniego rozwiązania,  albo problemy techniczne z komputerem.

Załóżmy, że nie mamy pojęcia jak ugryź kompletnie to zadanie, musimy przeprowadzić konsultacje. Przechodzimy wtedy do naszej historii użytkownika, do zakładki "Links" i naciskamy przycisk "New".


Uzupełniamy "Link Type" jako "Child" (bo Impediment będzie dzieckiem naszej historii użytkownika) oraz "Work Item Type" jako Impediment. Nazywamy go "Problem z metodą automatycznego wyświetlania aktualnej listy graczy" i naciskamy OK.


Pojawi się widok Impedimentu, dopisujemy opis (np. "Mam problem z wymyśleniem metody automatycznego wyświetlania aktualnej listy graczy, potrzebuję konsultacji z kimś bardziej doświadczonym."). Zapisujemy go i wracamy do widoku PBI. Musimy na nim zaznaczyć, że prace nad nim zostały wstrzymane. Dokonujemy tego poprzez oznaczenie jego pola "Blocked" na "Yes".

Załóżmy, że udało nam się ustalić, że powinniśmy rozwiązać problem
automatycznego wyświetlania listy gracz przy pomocy wzorca obserwatora. Przechodzimy do utworzonej wcześniej Przeszkody, wpisujemy nasze rozwiązanie w zakładkę "Resolution" i zmieniamy status na zamknięty ("Closed").

Możemy teraz przystąpić do programowania.

Implementacja

Implementację zaczynamy oczywiście od napisania testu akceptacyjnego (przykład będzie mocno uproszczony bo nie o to tutaj chodzi - więcej na temat testów jednostkowych możecie znaleźć we wpisach tutaj i tu). Postępujemy zgodnie z tym co zawarliśmy w testach akceptacyjnych oraz Impedimencie. Opis wzorca obserwator można znaleźć przykładowo tutaj.
Test może wyglądać następująco:

[TestMethod]
public void AddPlayersPrintValidInformations()
{
    var mocks = new MockRepository();

    var playersManagers = new PlayersManager();
    var printer = mocks.StrictMock<Player>();
    var playersObserver = new PlayersObserver(printer);

    playersObserver.Observe(playersManagers);
            
    playersManagers.Attach(playersObserver);
            
    //Dodanie pierwszego gracza
    Expect.Call(()=> printer.Print("1: Jan Kowalski"));
            
    //Dodanie drugiego gracza
    Expect.Call(() => printer.Print("1: Jan Kowalski"));
    Expect.Call(() => printer.Print("2: Krzysztof Krawczyk"));
            
    //Dodanie trzeciego gracza
    Expect.Call(() => printer.Print("1: Jan Kowalski"));
    Expect.Call(() => printer.Print("2: Krzysztof Krawczyk"));
    Expect.Call(() => printer.Print("3: Eustachy Janicki"));

    mocks.ReplayAll();

    //Dodanie pierwszego gracza
    playersManagers.AddPlayer(new Player { Name = "Jan Kowalski" });
            
    //Dodanie drugiego gracza
    playersManagers.AddPlayer(new Player { Name = "Krzysztof Krawczyk" });
            
    //Dodanie trzeciego gracza
    playersManagers.AddPlayer(new Player { Name = "Eustachy Janicki" });

    mocks.VerifyAll();
}

Oczywiście przy pisząc test postępujemy zgodnie z metodologią TDD, po kolei uzupełniając definicje klas i metod. Przykładowa ich implementacja mogła by wyglądać:

public class PlayersObserver : IPlayersObserver
{
    private readonly IPrinter _printer;
    private IPlayersManager _playersManager;

    public PlayersObserver(IPrinter printer)
    {
        _printer = printer;
    }

    public void Observe(IPlayersManager playersManager)
    {
        _playersManager = playersManager;
    }


    public void Update()
    {
        var players = _playersManager.GetPlayers();

        var i = 0;

        foreach (var player in players)
        {
            _printer.Print(
                String.Format(
                    "{0}: {1}", ++i, player.Name));
        }
    }

    public void OnPrintState(string info)
    {
        Console.WriteLine(info);
    }
}

public class PlayersManager : IPlayersManager
{
    private readonly IList<IPlayersObserver> _observers 
        = new List<IPlayersObserver>();

    private readonly IList<Player> _playersList = new List<Player>();

    public void Attach(IPlayersObserver observer)
    {
        _observers.Add(observer);
    }

    public void Detach(IPlayersObserver observer)
    {
        _observers.Remove(observer);
    }

    public void AddPlayer(Player player)
    {
        _playersList.Add(player);
        foreach(var observer in _observers)
        {
            observer.Update();
        }
    }

    public IList<Player> GetPlayers()
    {
        return _playersList;
    }
}

W tym momencie kończy się proces implementacji. Nasze testy przechodzą. Możemy zrobić Check in naszych zmian. Przechodzimy do Team Explorera, naciskamy "Pending Changes".


Pierwszą rzeczą jest dodanie komentarza (nie zapominajmy do tym!). Kolejną powiązanie naszych zmian z zadaniami. Naciskamy "Add Work Item by ID" i dodajemy zarówno nasze zadanie jak i PBI. Przy wiązaniu zmian mamy dwie opcje:
- Associate - po prostu "doklejamy" informację o tym, że zmiana dotyczy danego Work Itema (WI)
- Resolve - robi to samo co Associate i dodatkowo zmienia status WI na rozwiązany
Ponieważ uznajemy nasze zadanie za skończone zaznaczamy je jako Resolve. Decyzję o zakończeniu historii użytkownika zgodnie z metodyką Scrum podejmuje Właściciel Produktu, dlatego też w przypadku PBI zaznaczamy tylko Associate.
Gdy nasze zmiany wejdą z sukcesem pozostaje nam tylko jedna rzecz do zrobienia - podpięcie naszego testu akceptacyjnego do zdefiniowanego Test Case'a. Otwieramy informacje o nim i przechodzimy do zakładki "Associated automation".


Wybieramy nasz test jednostkowy poprzez przycisk koło pola "Automated test name".
Poprawiamy wartość effort przy zadaniu oraz PBI i jeżeli wszystko zrobiliśmy poprawnie zakończyliśmy tym samym nasz proces implementacji.

Testy

Po zakończeniu implementacji (zwykle na koniec Sprintu) Właściciel Produktu testuje czy wszystkie historie użytkownika zostały zrealizowane poprawnie.
W naszym przypadku okazuje się, że wystąpił błąd. W opisie testu akceptacji było napisane, że:

"Dodajemy gracza "Jan Kowalski" poprzez metodę AddPlayer klasy PlayersManager - Wyświetlona informacja: "01. Nazwa: Jan Kowalski"
 
My w trakcie implementacji nie zauważyliśmy, że numery mniejsze od 10 mają mieć zero na początku. Proces zgłaszania i rozwiązania wygląda następująco:
1. Właściciel produktu dodaje teraz błąd powiązany z naszą historią użytkownika (w sposób analogiczny jak podpinaliśmy impediment).
2. My, po analizie błędu oceniamy ile czasu zajmie jego poprawienie, uaktualniamy effort dla PBI oraz buga
3. Dodajemy zadanie-dziecko do błędu (analogicznie jak zadanie do historii użytkownika)
4. Gdy rozwiążemy błąd i wrzucamy nasze zmiany spinamy je zarówno z zadaniem,błędem jak i historią użytkownika.
5. Gdy Właściciel Produktu zaakceptuje rozwiązanie zamyka błąd i oznacza historię użytkownika za zrealizowaną.

Podsumowanie

Mam nieodparte wrażenie, że ten artykuł jest z jednej strony za krótki, z drugiej, że za długi. Dlaczego? Za krótki gdyż temat jest tak rozległy, że można by było napisać książkę i to by było mało. Za długi, bo nie wiem czy ilość szczegółów nie zamaże obrazu całości.
Mam jednak nadzieję, że udało mi się jednak przekazać ideę oraz że lektura mojego artykułu pozwoli Wam zrozumieć jak można zamodelować proces tworzenia funkcjonalności w TFS zgodnie z TDD i metodyką Scrum.

sobota, 3 grudnia 2011

Scrum i Team Foundation Server cz.5 - Continuous Integration

1. Trochę teorii

Każdy z nas w trakcie zawodowej kariery miał (nie)przyjemność pracować z systemami kontroli wersji. Codziennie w trakcie swojej pracy wrzucamy, pobieramy z nich pliki, rozwiązujemy konflikty. Wiemy po co je używać, ale czy wiemy jak? Tak jak w każdej dziedzinie życia pewne rzeczy można robić lepiej lub gorzej. W tym artykule postaram się pokazać kilka dobrych zasad ciągłej integracji (Continuous Integration), które pozwolą usprawnić trochę ten proces. Na koniec przedstawię przykład jak wdrożyć te zasady w Team Foundation Server.

Systemy kontroli wersji są czymś o czym powinniśmy pomyśleć na samym początku projektu. Mają wiele zalet, nie można się bez nich obyć, "ale...". Tym "ale" jest najbardziej zawodny element procesu - ludzie. 
Każdy z nas nie raz klął na kolegę, który wrzucił niebudujący się kod do repozytorium, drżał przed nadchodzącymi konfliktami przy pobraniu nowej wersji repozytorium. Myślę, że każdy z nas mógłby wymienić z imion współpracowników specjalizujących się we wrzucaniu niebudującego się kodu. 

Na bazie swoich doświadczeń uważam, że sporo z tych błędów można wyeliminować poprzez stosowanie się do kilku prostych zasad:

1. W repozytorium znajduje się tylko kompilujący się kod - przed wrzuceniem swoich zmian, poświęćmy czas i przebudujmy chociaż projekt. Nie ma nic bardziej irytującego niż niebudujący się kod. Nieszczęśliwcowi, który ściągnie rewizję w takiej sytuacji zwykle pozostaje cofnięcie się do poprzedniej działającej wersji, lub zrobienie hacku.

2. W repozytorium znajduje się tylko działający kod - jest to rozwinięcie pierwszej zasady. Sam fakt, że kod się buduje nie oznacza, że działa poprawnie. Przed wrzuceniem wersji poświęćmy chwilę na sprawdzenie czy nasz kod działa poprawnie: odpalmy testy, przeklikajmy się przez modyfikowane moduły. Często wydaje nam się to stratą czasu, bo "przecież to była tylko mała zmiana", a potem okazuje się, że zmianę zrobiliśmy na szybko i wrzuciliśmy kod, który wywala pół aplikacji. 
Oczywiście, niektórzy powiedzą, że czasami nie da się tego zrobić, gdy więcej niż jedna osoba pracuje nad tym samym kodem i muszą się nim dzielić. Repozytoria kodu mają mechanizmy pozwalające na rozwiązanie tych problemów: tworzenie osobnych gałęzi kodu (branch), odkładanie kodu na półki (shelve). Jeżeli zastosowanie tych rozwiązań jest zbyt kłopotliwe - zróbmy wszystko by okres "zepsutego" repozytorium trwał najkrócej.

3. Wrzucajmy regularnie i często nasze zmiany - oczywiście musimy zachować pierwsze dwie zasady. Dzielmy nasz kod na małe fragmenty (units of code). Oprócz takich zysków jak lepsza jakość kodu, zmniejszamy szansę na kłopotliwe rozwiązywanie złożonych konfliktów.

4. Im częściej robimy update - tym lepiej - jest to szczególnie kluczowe, gdy robimy dużą zmianę, mającą duży wpływ na działanie systemu. Musimy przeprowadzić dużo testów, nie możemy wtedy szybko wrzucić zmian. Im częściej będziemy pobierać zmiany tym szybciej połączymy się z kodem innych i szansa na konflikty zdecydowanie zmaleje.

5. Piszmy komentarze gdy wrzucamy kod - niby drobna rzecz, ale bardzo pomaga gdy musimy znaleźć przyczynę modyfikacji fragmentu kodu. Napiszmy z jakim zadaniem nasze zmiany są związane, co zmodyfikowaliśmy, dodaliśmy, usunęliśmy tym lepiej. Krótko ale treściwie.

Niestety teoria teorią, a praktyka praktyką. Często okazuje się, że sporo ludzi podchodzi do nich nonszalancko. Czasami skomplikowanie systemu nie pozwala w prosty i szybki sposób na sprawdzenie wpływu naszych zmian na jego działanie.

Z pomocą przychodzą nam zasady i systemy Continuous Integration. Oprócz zasad dobrego używania kodu (m.in. tych, które wymieniłem powyżej) główną ideą jest to, że zmiany w kodzie powinny być ciągle integrowane ze sobą. Powinna być regularnie sprawdzana jego spójność, integralność oraz poprawność działania. 

Dokonuje się tego przez regularne buildy projektu, uruchamianie testów itd. Powinny się one odbywać w środowisku identycznym do docelowego klienckiego (eliminujemy w ten sposób błędy wynikające z tego, że programiści mają różne konfiguracje swoich systemów). Każda zmiana powinna zostać przetestowana. Jeżeli nie spełni kryteriów akceptacji to powinna nie zostać dopuszczona do repozytorium, lub zespół projektowy powinien zostać niezwłocznie powiadomiony o pojawieniu się błędnego kodu.

Postaram się pokazać jak te cele można osiągnąć przy pomocy Team Foundation Server. Pokażę jak utworzyć dwie definicje buildów, które powinny rozwiązać większość podstawowych problemów:
- build dzienny - będzie raz na dobę sprawdzał czy projekt buduje się, czy wszystkie testy przechodzą. Ponieważ może on trwać długo, uruchamiany będzie w nocy - by nie utrudniać pracy programistom.
- build wejśćiowy (gated build) - będzie sprawdzał czy kod wrzucany jest poprawny. Jeżeli nie spełni zadanych kryteriów, nie dostanie się do repozytorium. Jakie warunki będziemy sprawdzać? Pierwszym będzie kompilacja projektu, drugim przejście najważniejszych testów (uruchamianie wszystkich nie było by dobrym posunięciem, bo trwało by zdecydowanie za długo).

2. Trochę praktyki


Pierwszym krokiem jest utworzenie projektu i dorzucenie go do Team Foundation Server. Bazował będę na przykładzie opisanym w "Scrum i Team Foundation Server cz.4 - Tworzymy projekt".

Pierwszą czynnością jest utworzenie folderu, do którego będą wrzucane DLLki budowanych solucji. Na nim będzie działał serwer. Załóżmy folder na dysku C: o nazwie "BuildFolder". Konieczne jest nadanie maksymalnych uprawnień do folderu procesowi buildów TFS (dla mnie będzie to "Local Service"). Oprócz tego musimy udostępnić w sieci lokalnej ten folder.


Mając już skonfigurowany folder możemy przejść do właściwej konfiguracji. Otwieramy Visual Studio i otwieramy Team Explorera (Menu => View => Team Explorer)


Przechodzimy do menu konfiguracji buildów naciskając przycisk "Builds". Aby dodać nową definicję naciskamy przycisk "New Build Definition". Dodajemy dwie definicje:

1. Build dzienny 

W  pierwszym oknie wpisujemy jego nazwę (np. "OneCardMaster - Daily" i ustalamy, że ma być aktywny zaznaczając opcję "Enabled".

Przechodzimy do zakładki "Trigger". 


W niej definiowane wyzwalacze dla naszego buildu. Chcemy, żeby uruchamiał się codziennie o godzinie 3 w nocy, nawet jeżeli kod nie zmienił się. Zaznaczamy opcje jak na obrazku powyżej. 
Przechodzimy do zakładki Build Defaults. I podajemy w niej adres sieciowy utworzonego przez nas wcześniej katalogu do buildów.


W zakładkach Workspace, Process i Retention Policy zostawiamy wartości domyślne.
Naciskamy przycisk zapisz i mamy skonfigurowany pierwszy build.


2. Build wejściowy


Tworzymy kolejną definicję builda poprzez Team Explorera. Podajemy jej nazwę "One Card Master - Gated". Zakładki Workspace, Build Defaults, Retention Policy tak samo jak w przypadku builda dziennego.
Przechodzimy do zakładki Trigger i ustalamy w niej opcję "Gated Check-in".




Następnie naciskamy w Process. Rozwijamy kolejno sekcje: Basic, Automated Tests, Test Assembly.




Ustalamy, że check-in ma być odrzucony jeśli wybrane przez nas testy nie przejdą ("Fail Build On Test Failure" - True). 
Aby wybrać testy, które mają być wywoływane wpisujemy w opcję "Category Filter" jej nazwę np. "VIT" (Very Important Tests).
Zapisujemy builda i "eto wsio".


3. Trochę przykładów

Aby zaprezentować jak działa nasz build utwórzmy projekty o strukturze.

Projekt OneCardMaster jest to "Class Library", OneCardMasterTest - "Test Project".
Dodajemy klasę ClassToTest wyglądającą:
public static class ClassToTest
{
    public static bool VeryImportantFunction()
    {
        return true;
    }

    public static bool Function1()
    {
        return true;
    }

    public static bool Function2()
    {
        return true;
    }

    public static bool Function3()
    {
        return true;
    }
}

Jak widać jest to prosta klasa, która jedynie symuluje działanie systemu. Mamy tutaj cztery funkcje w tym jedną bardzo ważną. Do projektu testowego dodajemy klasę sprawdzającą poprawność jej działania.
[TestClass]
public class ClassToTestTest
{
    private TestContext testContextInstance;

    public TestContext TestContext
    {
        get
        {
            return testContextInstance;
        }
        set
        {
            testContextInstance = value;
        }
    }

    [TestMethod]
    [TestCategory("VIT")]
    public void VeryImportantFunction_ReturnsTrue()
    {
        Assert.IsTrue(ClassToTest.VeryImportantFunction());
    }

    [TestMethod]
    public void Function1_ReturnsTrue()
    {
        Assert.IsTrue(ClassToTest.Function1());
    }

    [TestMethod]
    public void Function2_ReturnsTrue()
    {
        Assert.IsTrue(ClassToTest.Function2());
    }

    [TestMethod]
    public void Function3_ReturnsTrue()
    {
        Assert.IsTrue(ClassToTest.Function3());
    }
}

Do każdej metody dodana została funkcja sprawdzająca jej poprawność. Jak łatwo zauważyć na ten moment wszystkie testy przechodzą.
Przechodzimy do Team Explorera i naciskamy przycisk Check In. Po zaakceptowaniu pojawi nam się nowe okno (jest to zmiana w stosunku do tego co mieliśmy przed konfiguracją builda wejściowego).




Informuje nas ono o tym, że nasze pliki zostaną wrzucone "na półkę". TFS sprawdzi poprawność zmian i jeżeli weryfikacja przebiegnie poprawnie to doda je do repozytorium. Naciskamy przycisk Build Changes.
W naszym systemie zostanie uruchomiony wątek sprawdzający czy build się nie zakończył.




Gdy status builda się zmieni otrzymamy o tym informację w postaci wyskakującego okna. Jeżeli wszystko zrobiliśmy poprawnie powinno wyglądać następująco:




Mamy dwie opcje do wyboru:
- Reconcile - odświeża status naszej lokalnej kopii repozytorium, czyści zmiany z pending changes itd.
- Ignore - nic nie robi - nie odświeża repozytorium. Dopiero gdy zrobimy update to uaktualni nam się wersja


Spróbujmy teraz zmienić metodę VeryImportantFunction na:


public static bool VeryImportantFunction()
{
    return false;
}


Po takiej zmianie nasz test z kategorii "VIT" nie będzie przechodził i build wejściowy nie powinien się udać.
Przejdźmy do Team Explorer i zróbmy Check In. Po zakończeniu builda powinniśmy zobaczyć okno informujące nas o niepowodzeniu:




Podsumowanie

W powyższym artykule starałem się przedstawić zasady dobrego korzystania z repozytorium, podstawy Continuous Integration oraz przykład jak zastosować to w praktyce przy użyciu TFS. Oczywiście jest to tylko wstęp, bo temat jest długi jak rzeka. Raczej nie będziecie znać po jego lekturze wszystkich odpowiedzi, ale liczę, że będziecie chociaż wiedzieć o co pytać.

czwartek, 17 listopada 2011

Scrum i Team Foundation Server cz.3 - Wystawiamy się na zewnątrz

Tym razem wpis będzie poświęcony dodatkowej czynności, nie związanej bezpośrednio ze Scrum albo TFS. Pokażę prosty sposób w jaki można udostępnić swój serwer nie posiadając stałego łącza IP, łącząc się przez router. Nie uważam, że jest to opcja najlepsza, ani najbezpieczniejsza, ale jest prosta i może być punktem wyjściowym do lepszej konfiguracji.
Jak zrobić, żeby można było się połączyć z serwerem gdy nie posiada on stałego numer IP? Można to zrobić przy pommechanizmu DynDNS. Jeżeli chcemy uruchomić serwer np. ze stroną www lub podglądem kamer monitoringu dostępny z każdego miejsca w Internecie, a nie posiadamy stałego adresu IP, tylko otrzymujemy go dynamicznie z serwera DHCP (jak np. w przypadku Neostrady) musimy skorzystać z usługi DDNS (Dynamic Domain Name System) czyli usługi dynamicznego serwera nazw. 

Jak działa DDNS? Opis tego mechanizmu można znaleźć np. na stronie dipol.com.pl. Oto wycinek z niego:

"Z pomocą idzie usługa DDNS, która tak jak DNS posiada bazę danych z wpisami zależności adresu domenowego z numerycznym, ale może być aktualizowana w dowolnej chwili czasu przez posiadacza domeny. Dzięki temu serwer może być osiągalny pod jedną, ustaloną nazwą niezależnie od tego, jaki adres IP w danej chwili posiada. Z tego powodu z serwerem można komunikować się tylko za pomocą adresu domenowego przetłumaczonego przez serwer DDNS (chyba, że znamy aktualny adres numeryczny serwera, ale nigdy nie wiemy jak długo będzie on obowiązywał)."

UsuW tym tutorialu przedstawię jak skonfigurować DynDNS przy pomocy serwisu www.no-ip.com.

Utworzenie konta i konfiguracja na Dyn.com

Tak jak zostało wspomniane wcześniej, aby móc korzystać z DynDNS konieczne jest utworzenie konta na stronie dostarczającej tą usługę. Proces ten najlepiej przeprowadzić na komputerze, który chcemy wystawić jako serwer na zewnątrz.

1. Wchodzimy na stronę www.no-ip.com/services/managed_dns/free_dynamic_dns.html. Powinna ona wyglądać następująco:




Wpisujemy nasz adres e-mail i naciskamy przycisk "Sign Up Now!".

2. W kolejnej stronie wypełniamy naszymi danymi formularz rejestracji. Akceptujemy warunki licencyjne i naciskamy przycisk "I Accept, Create my Account".


3. Jeżeli wszystko wprowadziliśmy poprawnie pojawi nam się ekran informujący o konieczności potwierdzenia podanego przez nas adresu e-mail.


4. Wchodzimy na swoją skrzynkę pocztową i naciskamy link aktywacyjny w mailu, który otrzymaliśmy od portalu NO-IP. Po kliknięciu powinna pojawić się strona:




Naciskamy link "login".

5. W kolejnym ekranie wpisujemy swoje dane autoryzacyjne i naciskamy przycisk "Login".




6. W kolejnym widoku naciskamy przycisk "Add a Host".




7. Ukaże nam się ekran konfiguracji naszego adresu. Do celów naszej konfiguracji wystarczą nam domyślne wartości. Jedyne co musimy zrobić do wpisać unikatowy identyfikator np. "testtutorialscrumitfs" i nacisnąć przycisk "Create Host".


Udało nam się zarejestrować nasz serwer w serwisie DynDNS. Kolejnym krokiem jest:

Skonfigurowanie routera do obsługi DynDNS

Nowe routery zwykle dają możliwość konfiguracji DynDNS. Dzięki niej po zmianie adresu IP routera powiadomi on o tym automatycznie serwis DynDNS. 
Przykładowa konfiguracja zostanie pokazana na przykładzie modelu DI-524 firmy D-Link.

1. Otwieramy stronę konfiguracyjną routera pod adresem http://192.168.0.1/ . Standardowo dane autoryzacyjne to dla niego:
- login - admin
- hasło - admin
Otworzy nam się strona:


Wchodzimy do zakładki "Advanced".

2. Na następnym ekranie naciskamy przycisk "DDNS"


3. Pojawi nam się ekran do konfiguracji DynDNS. Wybieramy w nim serwis No-IP.com. Wpisujemy nasz hostname (np. "testtutorialscrumitfs") oraz swój login i hasło. Zatwierdzamy przyciskiem "Apply".


Po tych krokach skonfigurowaliśmy nasz router do współpracy z serwisem DynDNS. Pozostało, jednak, jeszcze kilka rzeczy do zrobienia.

Konfiguracja przekierowania portów

Team Foundation Server nasłuchuje pod portem 8080 na naszym serwerze. Gdy użytkownik będzie chciał połączyć się z Team Foundation serwer pod adresem DynDNS łączyć się będzie z routerem. Musimy skonfigurować go tak, żeby wiedział, o konieczności przekierowania połączenia z portem 8080 na nasz serwer (tzw. port forwarding) . Oto jak można to skonfigurować na routerze DI-524 firmy D-Link.

Wchodzimy na stronę konfiguracji routera (http://192.168.0.1/) i przechodzimy do ustawień zaawansowanych (zakładka "Advanced")


Wpisujemy w kolejne pola:
- Name - nazwę przekierowania (np. TFS)
- Private IP - wpisujemy IP naszego serwera
- Protocol Type - TCP
- Private Port - 8080
- Public Port - 8080
- Schedule - Always
Naciskamy przycisk "Apply". Voilà!

Konfiguracja Firewalla na serwerze

Pozostała nam jeszcze ostatnia rzecz. Skonfigurowanie naszego serwera tak by przyjmował połączenia z zewnątrz na porcie 8080.
Konfiguracja zostanie pokazana na przykładzie Windows Server 2008.
1. Wchodzimy do ustawień Firewalla (Start=>Administrative Tools =>Windows Firewall wih Advanced Security)


2. Zobaczymy okno konfiguracji zasad Firewalla. Będziemy ustalać zarówno regułę połączeń przychodzących (Inbound Rules) jak i wychodzących (Outbound Rules).


Naciskamy przycisk "New Rule" znajdujący się w prawym górnym rogu.

3. Otworzy nam się wizard konfiguracji reguły Firewalla.


Wybieramy opcję "Port" i naciskamy przycisk "Next"

4. W kolejnym oknie zaznaczamy opcję "TCP" i wpisujemy konkretny port - 8080 ("Specific local ports"). Klikamy "Next".


5. Pozwalamy na połączenie z tym portem ("Allow the connection") i przechodzimy dalej.


6. Zostawiamy domyślne ustawienia dostępu (Domain, Private, Public) i przechodzimy do kolejnego ekranu.


7. W ostatnim ekranie wizarda wpisujemy nazwę (np. TFS) i pełni samozadowolenia naciskamy przycisk "Finish".


8. Pozostało nam przeprowadzić analogiczny proces dla reguły połączeń wychodzących. W głównym ekranie przechodzimy do "Outbound rules" i wciskamy przycisk "New Rule". Wizard jest taki sam jak w przypadku reguły przychodzącej, więc oszczędzę Wam i sobie mojej pisaniny wierząc, że sobie poradzicie.

Test poprawności działania konfiguracji

Najprościej przetestować konfigurację próbując się z TFS przez Visual Studio.
1. Otwieramy Visual Studio (np. wersję 11). Menu => Team => Connect To Team Foundation Server


2. W kolejnym oknie naciskamy przycisk "Servers"


3. Aby dodać połączenie do naszego serwera naciskamy przycisk "Add"


3. W popupie, który nam się pojawi wpisujemy adres naszego serwera (np. "testtutorialscrumitfs.no-ip.com"). I naciskamy OK. 


Jeżeli okno się bez problemów zamknie to znaczy, że nasza konfiguracja się powiodła. (Jeżeli pojawi się prośba o podanie loginu i hasło podajcie dane administratora systemu, na którym zainstalowany jest TFS)

Udało się? Gratuluję! 
Jeżeli nie (odpukać), to piszcie szczegóły w komentarzach.

niedziela, 13 listopada 2011

Scrum i Team Foundation Server cz.2 - Instalacja TFS

Tak jak zapowiedziałem w poprzednim wpisie, część druga mojej opowieści o Scrum i TFS będzie opisywała instalację oraz wstępną konfigurację Team Foundation Server. Po tych wpisach powinno być wiadomo co chcemy robić oraz czym, część trzecia przedstawi jak.
Przykłady będą przedstawione na podstawie Team Foundation  Server 11 Developer Preview.

Instalacja serwera TFS

1. Pierwszym krokiem jest pobranie pliku instalacyjnego TFS 11 DP. Dostępny jest bezpłatnie pod adresem:  

2. Po udanym pobraniu pliku uruchamiamy go. Pokaże nam się ekran:


 Akceptujemy licencję i naciskamy przycisk "Continue".

3. W kolejnym ekranie zaznaczamy, że chcemy mieć włączone aktualizacje i rozpoczynamy proces instalacji naciskając przycisk "Install Now".


 4. Rozpocznie się proces kopiowania plików instalacyjnych.



5. Po zakończeniu tego procesu pojawi nam się okno z wyborem rodzaju instalacji.




Dostępne są następujące tryby:
- Basic - powinniśmy ją wybrać gdy chcemy mieć skonfigurowany system kontroli wersji, zarządzanie zadaniami oraz serwisy buildów. Wybór tej instalacji pozwala nam na wybranie istniejącej już bazy danych lub instalację SQL Express, gdy nie mamy żadnej bazy na komputerze. Jeżeli chcemy mieć skonfigurowanego Share Point lub Reporting Services powinniśmy wybrać inny tryb instalacji
- Standard Single Server - instalacja ta różni się od instalacji podstawowej tym, że konfiguruje również Share Point
- Advanced - mamy w niej dostęp do wszystkich opcji konfiguracyjnych. Pozwala m.in. na konfigurację TFSa do użytkowania zewnętrznych serwerów SQL, wybór instancji baz danych, Reporting Services oraz Share Point, użycie autentykacji Kerberos
- Application-Tier Only - pozwala na dodanie kolejnej instancji Team Foundation Server do istniejącego i skonfigurowanego środowiska
- Upgrade - pozwala na uaktualnienie aktualnej wersji Team Foundation Server

Wybieramy instalację w trybie Basic i naciskamy przycisk "Start Wizard".

6. Pojawi się pierwszy ekran Basic Wizarda. Naciskamy w nim przycisk "Next"


7. Kolejny ekran pozwala na wybór czy chcemy zainstalować nową wersję bazy danych czy chcemy wybrać istniejącą instancję. W tym opisie zostanie zaprezentowana droga z zainstalowaną bazą danych. Zaznaczamy opcję "Use an existing SQL Server Instance" i naciskamy przycisk "Next".


8. W tym kroku powinniśmy wpisać nazwę instancji naszego serwera (w moim przypadku "WIN-MPMQ6E2DM0C"). Warto nacisnąć przycisk "Test", który sprawdzi, czy nasz serwer jest rzeczywiście dostępny, lub czy nie zrobiliśmy jakiejś literówki. Jeżeli po teście pojawi nam się zielona "fajka" możemy śmiało nacisnąć przycisk "Next".


9. Jeżeli wszystko przebiegło pomyślnie to powinien pojawić nam się ekran z podsumowaniem dotychczas dokonanych wyborów. Naciskamy przycisk "Next".


10. Następuje teraz proces intalacji/konfiguracji baz danych, serwera IIS oraz Firewalla. Po jego zakończeniu ekran powinien wyglądać:


Naciskamy przycisk "Next".

12. W kolejny ekranie otrzymujemy jakże przyjemny napis "Success". Udało nam się zainstalować i skonfigurować pomyślnie TFS.


Naciskamy przycisk "Close".

Konfiguracja proxy TFS

13. Skoro udało nam się "zainstalować i skonfigurować pomyślnie TFS" to skąd kolejny punkt? OK, trochę nagiąłem prawdę. Co prawda TFS już stoi i ma się dobrze dokofigurujemy jeszcze dwie rzeczy. Proxy serwera, które pozwoli nam zmniejszyć obciążenie naszego serwera oraz serwisy Buildów. Wna głównym ekranie (przedstawionym w punkcie 5) na "Configure Team Foundation Server Proxy" . Powinien pojawić nam się ekran:


Naciskamy przycisk "Next".

14. Wybieramy użytkownika, przy pomocy którego proxy będzie łączyło się z TFS. Użytkownik ten powinien kontem serwisowym oraz zostać dodany do grupy użytkowników mających uprawnienia do TFS. Co prawda nie do końca bezpiecznie, ale dla ułatwienia wybrałem konto "NT AUTHORITY\LOCAL SERVICE". Po wyborze naciskamy przycisk "Next"


15. W kolejnym ekranie określamy port, na którym ma działać Proxy oraz miejsce, w którym ma być przechowywany jego Cache. Możemy pozostawić domyślne wartości oraz nacisnąć przycisk "Next".


16. Zobaczymy podsumowanie opcji, które wybraliśmy. Możemy zweryfikować ich poprawność naciskając przycisk "Verify" lub gdy ufamy sobie bezgranicznie nacisnąć od razu przycisk "Next".


17. Nastąpi proces sprawdzania konfiguracji i jeżeli wszystko poszło pomyślnie, wszystko zostanie oznaczone na zielono i będziemy mogli nacisnąć przycisk "Configure".
18. Po udanej konfiguracji powinniśmy zobaczyć ekran:


Naciskamy przycisk "Next".

19. Po raz kolejny możemy poczuć się dumni. Odnieśliśmy kolejny sukces - skonfigurowaliśmy proxy dla naszego serwera TFS. Po jego odtrąbieniu możemy zamknąć okno naciskając przycisk "Close"


Konfiguracja serwisu Buildów

20. Tym razem darowałem sobie żart o zakończeniu procesu instalacji. Pozostało nam jeszcze bowiem skonfigurowanie serwisu Buildów. W głównym ekranie (patrz punkt 5) zaznaczamy "Configure Team Foundation Build Service" oraz naciskamy przycisk "Start Wizard". Pojawi nam się okno:


Jeżeli nie mamy na komputerze zainstalowanych innych serwerów TFS możemy bez zastanowienia nacisnąć przycisk "Next".

21. W kolejnym ekranie mamy możliwość wybrania liczbę "agentów buildów". Im więcej ich mamy tym więcej buildów będzie mogło być naraz wykonywanych. Należy wziąć jednak pod uwagę, że każdy proces budowania zużywa zasoby systemowe, więc jeżeli nie mamy mocnego komputera nie ma sensu zwiększać ich liczby. W tym tutorialu wybrałem rekomendowaną liczbę - 1. Po wybraniu liczby agentów naciskamy przycisk "Next".


22. Musimy wybrać również użytkownika, na którego koncie będzie działał serwer buildów oraz port, na którym będzie łączył się z TFS. Ja wybrałem systemowe konto "NT AUTHORITY\LOCAL SERVICE" oraz sugerowany port 9191.


Przycisk "Next" będzie standardowo dobrym wyjściem by przejść do kolejnego kroku.

23. Pojawi się znany już nam ekran z podsumowaniem wybranych opcji. Jeżeli jesteśmy dokonanych wyborów możemy śmiało nacisnąć "Next".


24. Instalator przetestuje teraz czy wszystkie nasze ustawienia były poprawne. Jeżeli wszystko przebiegnie bez problemów powinniśmy zobaczyć ekran z przyjemnie zielonymi kolorami.


Pozostaje nam tylko w następnym widoku nacisnąć przycisk "Configure".

25. Nastąpi teraz instalacja i konfiguracja potrzebnych składników. Po jego zakończeniu powinniśmy mieć wszystkie paski w kolorze zielonym tak jak na screenie widocznym poniżej.


26. Na koniec powinniśmy zobaczyć jakże miły napis "Success". Gdy już się nim nacieszymy możemy nacisnąć przycisk "Finish" i zakończyć proces konfiguracji.



Udało nam się zatem zainstalować Team Foundation System. Mamy już wiedzę teoretyczną, mamy narzędzie, w kolejnym wpisie dowiemy się jak z niego skorzystać.

 
Design by Free WordPress Themes | Bloggerized by Lasantha - Premium Blogger Themes | Online Project management