niedziela, 29 sierpnia 2010

IRepository Pattern - wzorzec repozytorium

Repository Pattern to jeden z ważniejszych wzorców w przypadku pisania aplikacji ASP.NET MVC.
Podczas pisania aplikacji w MVC, w której wykorzystujemy TDD często natrafiamy na problem testowania warstwy bazodanowej. Najczęściej są to dwa podstawowe problemy: szybkość oraz odwracanie zmian które zaszły podczas testowania dodawania/usuwania itp. Problemy te często przekładają się na zniechęcenie do tworzenia testów, jak i sporym utrudnieniu tworzenia testowalnych aplikacji.
Potrzebne jest więc rozwiązanie, które pozwoli na łatwe testowanie kodu na obiektach w pamięci, jak i łatwą integrację w późniejszym stadium z bazą danych. Wszystkie te problemy może rozwiązać implementacja wzorca Repository Pattern.

Realny problem:

W przykładzie uczestniczą dwie klasy: Person oraz Car. Klasa Person reprezentuje jakiegoś człowieka, klasa Car - samochód.
Założenia: aplikacja ma pracować z bazą danych i mieć możliwość wykonywania testów bez potrzeby łączenia się do niej.
Wykorzystamy więc tutaj wzorzec repozytorium. Trochę teorii na początek. Wzorzec repozytorium zakłada utworzenie osobnego repozytorium dla każdej z klas. Repozytorium porozumiewa się z naszymi zmapowanymi obiektami i wykonuje żądania użytkownika.
W naszym przykładzie możemy łatwo wyodrębnić szereg metod które będą wykorzystywane np. na obiekcie klasy Person:
AddPerson, DeletePersonById, UpdatePerson, GetAllPersons, GetPersonById, GetPersonCount itd.
Druga z klas - Car będzie mieć podobne metody.
Istnieje więc szereg podobnych metod. Możemy je wyodrębnić jako wspólny interfejs IRepository. Interfejs ten będzie bazowym dla pozostałych interfejsów: IPersonRepository, ICarRepository.
Następnie stworzymy konkretne klasy implementujące nasze interfejsy: FakePersonRepository, SqlPersonRepository, FakeCarRepository, SqlCarRepository. Zapewni nam to łatwość przy testowaniu przy użyciu frameworków NUnit oraz Mocq.

Tak więc po kolei. Kod dwóch wcześniej omówionych klas:

    public class Person
    {
        public int IdPerson { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public int BirthYear { get; set; }

        public List<Car> Cars { get; set; }
    }


    public class Car
    {
        public int IdCar { get; set; }
        public string Mark { get; set; }
        public int ProductionYear { get; set; }
    }

Teraz tworzymy interfejs IRepository:

Kod:

    public interface IRepository<T>

    {

        List<T> GetAll();

        T GetById(int id);

        int Count();

 

        void Add(T entity);

 

        void Update(T entity);

 

        void DeleteById(int id);

        void DeleteAll();

    }


    public interface IPersonRepository : IRepository<Person>

    {

        void AddCarToPerson(Person person);

    }


    public interface ICarRepository : IRepository<Car>

    {

    }


Tworzymy następnie klasy implementujące ICarRepository oraz IPersonRepository w wersji działającej w pamięci:

    public class FakeCarRepository : ICarRepository

    {

        private List<Car> _cars = new List<Car>();

 

        public List<Car> GetAll()

        {

            return _cars;

        }

 

        public Car GetById(int id)

        {

            return _cars.Single(x => x.IdCar == id);

        }

 

        public int Count()

        {

            return _cars.Count;

        }

 

        public void Add(Car entity)

        {

            _cars.Add(entity);

        }

 

        public void Update(Car entity)

        {

            Car c = _cars.Single(x => x.IdCar == entity.IdCar);

            c.Mark = entity.Mark;

            c.ProductionYear = entity.ProductionYear;

        }

 

        public void DeleteById(int id)

        {

            _cars.Remove(_cars.Single(x => x.IdCar == id));

        }

 

        public void DeleteAll()

        {

            _cars.Clear();

        }

    }


    class FakePersonRepository : IPersonRepository

    {

        private List<Person> _personList = new List<Person>();

 

        public List<Person> GetAll()

        {

            return _personList;

        }

 

        public Person GetById(int id)

        {

            try

            {

                return _personList.Where(x => x.IdPerson == id).SingleOrDefault();

            }

            catch (ArgumentNullException)

            {

                return null;

            }

        }

 

        public int Count()

        {

            return _personList.Count;

        }

 

        public void Add(Person entity)

        {

            _personList.Add(entity);

        }

 

        public void Update(Person entity)

        {

            Person p = _personList.Where(x => x.IdPerson == entity.IdPerson).SingleOrDefault();

            p = entity;

        }

 

        public void DeleteById(int id)

        {

            _personList.Remove(_personList.Where(x => x.IdPerson == id).SingleOrDefault());

        }

 

        public void DeleteAll()

        {

            _personList.Clear();

        }

 

        public void AddCarToPerson(Person person, Car car)

        {

            if (person.Cars == null)

            {

                person.Cars = new List<Car>();

            }

 

            person.Cars.Add(car);

        }

    }


W podobny sposób tworzymy klasy SqlPersonRepository. W tym przypadku jednak tworzymy klasę, która działa na bazie danych a nie w pamięci. Implementacja rozwiązania bazodanowego zależy przede wszystkim od wybranej technologi czy też ORM mappera.

Nasze rozwiązanie jest bardzo proste w dalszej pielęgnacji. Przy testowaniu korzystamy z naszych Faków, dla klienta dostarczamy wersję działającą bezpośrednio na bazie danych.

sobota, 28 sierpnia 2010

Wyrażenia lambda

Wyrażenia lambda pozwalają na uproszczenie zapisu do wymaganego minimum. Zobaczymy na przykładzie jak od zwykłej metody przejść do wyrażenia lambda.

Mamy następujący problem: chcemy w naszej liście znaleźć tylko parzyste liczby i zwrócić je jako nowa lista. Aby łatwo wykonać to zadanie możemy skorzystać z metody FindAll które przyjmuje jako argument typ Predicate(bool (T)target). Predicate jest tutaj delegatem przyjmującą jeden parametr i zwracającą bool. Tak więc zaczynajmy od najprostszej wersji:

    class Program
    {
        static void Main(string[] args)
        {
            List<int> intList = new List<int>
            {
                1,2,3,4,5,6,7,8,9,10
            };

            List<int> matchedInts = intList.FindAll(new Predicate<int>(IsEven));
            foreach (var item in matchedInts)
            {
                Console.WriteLine(item);
            }
        }

        private static bool IsEven(int i)
        {
            return i % 2 == 0;
        }
    }

Pytanie tylko czy takie rozwiązanie jest nam na rękę? Metodę IsEven wykorzystamy tylko raz w naszym programie więc po co ją zapisywać globalnie? C# umożliwia skorzystanie z metod anonimowych tak więc powyższą funkcję możemy zapisać jako:

    class Program
    {
        static void Main(string[] args)
        {
            List<int> intList = new List<int>
            {
                1,2,3,4,5,6,7,8,9,10
            };

            List<int> matchedInts = intList.FindAll(delegate(int i)
            {
                return i % 2 == 0;
            });

            foreach (var item in matchedInts)
            {
                Console.WriteLine(item);
            }
        }
    }

Rozwiązanie całkiem niezłe, jednak można jeszcze lepiej za pomocą wyrażeń lambda:

    class Program
    {
        static void Main(string[] args)
        {
            List<int> intList = new List<int>
            {
                1,2,3,4,5,6,7,8,9,10
            };

            List<int> matchedInts = intList.FindAll(i => { return i % 2 == 0; });

            foreach (var item in matchedInts)
            {
                Console.WriteLine(item);
            }
        }
    }

Całkiem nieźle. Dużo mniej kodu a taki sam efekt. Czas na składnię:
lista parametrów => instrukcje
i => {return i % 2 == 0;}

Teraz kilka faktów:
Jeżeli delegat nie przyjmuje parametrów nasze wyrażenie zapisujemy jako:
() => instrukcje
Jeżeli delegat przyjmuje więcej niż 1 parametr, delegatę zapisujemy jako:
(i, j, ...) => instrukcje
Jeżeli parametry nie można zidentyfikować po typie, możemy je przekazać jawnie:
(int i, string j) => instrukcje


C# dostarcza nam predefiniowane delegaty:
Func - jest to generyczny delegat, przyjmujący do 9 parametrów i zwracający TRes. Przykład:

            Func<int, int, int> AddTwoIntegers = (a, b) => { return a + b; };
            Console.WriteLine(AddTwoIntegers(5, 5));
Action - generyczna akcja przyjmująca do 16 parametrów i zwracajaca void:

            Action<int> action = x => { x = x * 5; Console.WriteLine(x); };
            action(10);
Predicate - jest to generyczny delegat przyjmujący jeden parametr i zwracający bool:

            Predicate<int> predicate = x => { return x % 2 == 0; };
            Console.WriteLine(predicate(21));

Zobaczmy jeszcze na jeden aspekt:
Podczas debugowania naszych metod możemy uświadczyć następującego widoku:

Metoda jest przekształcana do strumienia, można w tym przypadku wykorzystać klasę Expression i otrzymać bardziej czytelniejszy kod:


Jednym aspektem, który z pewnością nas zmartwi jest to, iż nie każdy Func może zostać przekształcony do drzewa decyzyjnego. Niektóre przykłady musimy sami przekształcać.

Różnice między IEnumerable i IQueryable

Ostatnio zastanawiałem się nad tym czy w swojej aplikacji zwracać obiekty zapytania LINQ jako IEnumerable czy też jako IQueryable.

IEnumerable jest pierwotnym interfejsem który wykorzystujemy w LINQ. Najważniejsze w nim jest to, że tworząc do niego zapytania działamy w pamięci lokalnej aplikacji. Tak więc tworząc zapytania do bazy danych z różnymi metodami jak where, take, skip itd. najpierw cała tabela jest kopiowana do pamięci a następnie są z niej wybierane szukane elementy.

IQueryable jest to idealny sposób zwracania zapytań w przypadku odnoszenia się do bazy danych. Buduje zapytanie do bazy które zawiera już w sobie wszelkie wcześniej wymienione metody (w języku sql napisane oczywiście).

Podsumowując:
IEnumerable - operacje w pamięci
IQueryable - LINQ to SQL, Entity Framework itp.

Kiedy używać PLINQ a kiedy Parallel.ForEach?

Na stronach Microsoftu można znaleźć ciekawy artykuł autorstwa Pameli Vagata na temat tego kiedy stosować PLINQ a kiedy pętle Parallel.ForEach.

Warto poświęcić trochę czasu i dokładnie przeczytać artykuł wraz z przykładami w nim zawartymi. Dowiemy się kiedy stosować wymienione rozwiązania jak i zobaczymy na prawdę ciekawie przygotowane przykłady.

Najważniejsze informacje zawarte w artykule (dla leniwych):
Parallel.ForEach stosujemy gdy:
- operacje mają wykonywać się niezależnie jak i chcemy ustawić ilość aktywnych wątków
- gdy chcemy mieć dostęp do lokalnie wykonywanego wątku (ograniczymy też przy tym koszty synchronizacji wątków)
- gdy chcemy przerwać działanie np. po znalezieniu szukanego elementu (korzystamy z metod Break i Stop)

PLINQ stosujemy gdy:
- chcemy zachować kolejność w kolekcji - PLINQ + metoda AsOrdered - przydatne jest to np. gdy dokonujemy konwersji klatek filmu, które powinny być ustawione w takiej samej kolejności na wyjściu jak i na wejściu
- gdy dokonujemy operacje na dwóch niezależnych kolekcjach - możemy wtedy skorzystać z metody Zip


Polecam jednak samemu przeczytać artykuł i zapamiętać zawarte tam informacje.

ASP.NET MVC 2 cz. 10 Ajax

Dzisiaj trochę o technologi Ajax czyli Asynchroniczny (od A w nazwie) JavaScript. Dzięki zastosowaniu tej technologii strona nie musi być w całości przeładowywana przez użytkownika a w wydzielonych częściach.

Kiedy więc warto korzystać z Ajaxu?:
- renderowanie poszczególnych części strony - mając do czynienia ze skomplikowaną stroną html, warto zastanowić się czy nie podzielić ją na części i korzystając z Ajaxu renderować te części z którymi aktualnie prowadzi interakcję użytkownik. Dzięki temu odciążamy serwer.
- odczucie, że witryna działa szybciej - nie wysyłamy całej strony do serwera a tylko fragment. Dzięki temu przeładowuje się tylko ta wysyłana część co sprawia wrażenie szybszego jej działania.
- odciążenie serwera (nie trzeba ponownie odpytywać bazy danych w celu załadowania całej strony), zmniejszenie ilości połączeń
- asynchroniczność działania witryny - interakcja ze stroną w sposób jaki znamy z aplikacji deskopowych (część informacji może sama się zmieniać w przeglądarce)

Oczywiście nic nie jest bez wad. Ajax niesie także za sobą takie niebezpieczeństwa jak:
- wymuszenie włączonego JavaScriptu w przeglądarce klienta - jeżeli cała witryna jest wykonana w technologii Ajax, a użytkownik ma wyłączoną obsługę JavaScript, użytkownik nie będzie miał możliwości interakcji z naszą witryną
- strata funkcjonalności klawiszy wstecz/następny oraz funkcjonalności zakładek - korzystając z Ajax najczęściej tracimy możliwość wykorzystania funkcji przeglądarki oferujących możliwość cofnięcia się na stronie jak i dodania strony do zakładek. W przypadku korzystania z Ajaxu pasek adresu nie zmienia się, więc zapamiętanie aktualnie przeglądanej informacji jest niemożliwe.
- większa ilość przesyłanych danych - pliki JavaScrpt mogą mieć duże rozmiary co dodatkowo powiększa ilość danych które musi przesłać serwer jak i odebrać użytkownik.


Mając na uwadze podane powyżej zalety i wady warto zastosować prosty wzorzec tworzenia stron wykorzystujących Ajax:
- każde zapytanie Ajax używa osobnej akcji w kontrolerze
- w akcji sprawdzamy czy zapytanie jest Ajaxowe
- każda akcja zwraca dedykowany widok o ile zapytanie nie jest Ajaxowe
Dzięki takiemu postępowaniu unikniemy problemów w przypadku gdy użytkownik ma wyłączoną obsługę JavaScript.

Aby było możliwe korzystanie z Ajaxu w naszej aplikacji MVC dodajemy referencję do plików: MicrosoftAjax.js oraz MicrosoftMvcAjax.js:
    <script src="/Scripts/MicrosoftAjax.js" type="text/javascript"></script>
    <script src="/Scripts/MicrosoftMvcAjax.js" type="text/javascript"></script>

Jak już mamy dodane referencje czas na napisanie przykładu, który pokaże jak posługiwać się Ajaxem w praktyce:
Na początek stworzymy zwykłą formę, która umożliwia wpisanie użytkownikowi frazy (np. poszukiwanej rzeczy) i zwróci prostego stringa:
    <% using (Html.BeginForm(new { action = "MojaAkcja" }))
       { %>
       <input type="text" name="zapytanie" />
       <input type="submit" value="Wyślij" />
    <% } %>

    <div id="rezultat">

    </div>
        public string MojaAkcja(string zapytanie)
        {
            return "Wpisałeś: " + zapytanie;
        }

Przerobienie powyższego kodu na wykorzystujący Ajax sprowadza się do:
    <% using (Ajax.BeginForm("MojaAkcja", new AjaxOptions { UpdateTargetId = "rezultat" }))
       { %>
       <input type="text" name="zapytanie" />
       <input type="submit" value="Wyślij" />
    <% } %>

    <div id="rezultat">

    </div>

Jak widać efekt uzyskaliśmy zmieniając zaledwie jedną linijkę. Oczywiście jest haczyk - jeżeli użytkownik nie będzie mieć włączonej obsługi JavaScript zostanie mu wyświetlona tylko zawartość metody MojaAkcja. Tak więc zniknie MasterPage i wszelka grafika a zostanie 1 linijka tekstu. Tego raczej nie chcielibyśmy.
Z pomocą przychodzi nam sam JavaScript który dodaje do wysyłanych nagłówków informacje o tym, że żądanie miało miejsce przy użyciu Ajaxa. W naszej akcji możemy to sprawdzić za pomocą pola IsAjaxReguest:
        public ActionResult Index(string zapytanie = "")
        {
            ViewData["dane"] = zapytanie;
            return View();
        }

        public ActionResult MojaAkcja(string zapytanie)
        {
            if (Request.IsAjaxRequest())
            {
                return Content("Wpisałeś: " + zapytanie);
            }

            return RedirectToAction("Index", new { zapytanie = zapytanie});
        }

Teraz w zależności od tego czy użytkownik ma włączoną obsługę czy też nie, zostanie przeładowana cała witryna bądź też wysłany będzie mały fragment. Oczwiście metod jest kilkanaście do wybory (np. ActionLink, RouteLink).

Zwykłe podmienianie zawartości diva to oczywiście nie wszystko co można osiągnąć za pomocą Ajaxa. Aby osiągnąć więcej można pobrać Ajax Control Toolkit, skopiować skrypty do folderu Scripts i korzystać z wielu gotowych kontrolek.

Wszystko co można osiągnąć za pomocą Ajax można także stworzyć korzystając z jQuery. To do nas należy którego z frameworków będziemy używać. metodoa IsAjaxRequest działa zarówno dobrze dla Microsoft Ajax jak i dla jQuery.

Dostajemy więc potężne narzędzie udostępniajace ogromne możliwości.

czwartek, 26 sierpnia 2010

ASP.NET MVC 2 cz. 9 jQuery

jQuery to biblioteka która ułatwia pisanie aplikacji internetowych z wykorzystaniem JavaScriptu. Podczas projektowania tej biblioteki, jej twórcy przyświecały następujące cele:
- napisz mniej, uzyskaj duży rezultat
- lekkość i łatwość użycia
- kompatybilność między przeglądarkami
- prostota dodawania nowych rzeczy

jQuery posiada wsparcie w Microsoft ASP.NET 4.0. Dzięki temu uzyskujemy wsparcie IntelliSense.

Oczywiście oprócz zalet jQuery są też i wady:
- dodatkowe dane do załadowania
- spadek wydajności w porównaniu do czystego JavaScriptu

Biorąc jednak zalety można śmiało powiedzieć, że zakrywają one wszystkie wady. Warto podczas korzystania z tej biblioteki korzystać z wersji dostępnych na CDN googla czy Microsoft. Dzięki temu mamy szansę, że inni użytkownicy mają już scachowany plik w swojej przeglądarce.

Czas na podstawy języka i kilka przykładów.
Na początek dodajemy referencję do pliku biblioteki oraz dokumentacji, z której korzysta następnie IntelliSense:
   <script src="http://ajax.microsoft.com/ajax/jquery/jquery-1.4.1.min.js" type="text/javascript"></script>
   <% /* %><script src="~/Scripts/jquery-1.4.1-vsdoc.js"></script><% */ %>

Linijki te wpisujemy do sekcji head naszej strony (lub MasterPage w moim przypadku).
Tutaj jedna uwaga. W większości przeglądarek kod JavaScript uruchamia się przed załadowaniem całego kodu HTML. aby zapobiec temu w jQuery stosujemy składnię:
   <script type="text/javascript">
       $(function () {
        //kod
       });
   </script>


1. Podstawy języka
Zapytanie jQuery:
[zmienna=]$(selektor).metoda(argument, funkcja){
$(selektor).metoda(...)
...})
Składnia jest banalnie prosta, zobaczmy przykład:
    <script type="text/javascript">
        $(function () {
            $("div").wrapInner("<h3></h3>");
        });
    </script>

    <div>Ala ma kota</div>

Po uruchomieniu zobaczymy:

Tak więc działanie powyższego kodu można opisać w następujący sposób:
Znajdź wszystkie elementy div i ich zawartość wprowadź między znaczniki <h2></h2>
Inny przykład:
    <script type="text/javascript">
        $(function () {
            $("#menu").width(500);
        });
    </script>

Powyższy przykład czytamy: Znajdź element o id = #menu i nadaj mu szerokość 500. Teraz jeżeli chcielibyśmy nadać wszystkim akapitom w elemencie o id #menu nadać kolor czerwony, zapewne napisalibyśmy to tak:
        $(function () {
            $("#menu").width(500);
            $("#menu p").css("color", "Red");
        });

Kod ten jest jednak nieefektywny w przypadku dużych dokumentów HTML. Należy pamiętać, że dokument jest przeszukiwany od góry do dołu. Można przyśpieszyć operację i nie wykonywać ponownego szukania fragmentu oznaczonego #menu poprzez zapisanie jego pozycji w zmiennej:
        $(function () {
            var menu = $("#menu").width(500);
            $("p", menu).css("color", "Red");
        });


2. Selektory
Jest to podstawowa możliwość przyporządkowywania działania jQuery do konkretnych elementów strony. Do dyspozycji mamy całą ich gamę:
* - każdy element
E - elementy typu E
#id - element o id = #id
.class - elementy o klasie class
E F - każdy element F który jest potomkiem E (np. ("div p") wybiera wszystkie p znajdujące się w div)
E > F - każdy F, który jest bezpośrednio dzieckiem E
E[foo] - każdy element E z atrybutem foo
E[foo = "something"] - każdy E z atrybutem foo równym something


3. Filtry
jQuery po znalezieniu pasujących elementów do naszego zapytania, pozwala je filtrować. Do dyspozycji mamy następujące filtry:
:even - nieparzyste elementy
:not() - wszystkie elementy oprócz podanych
:contains('') - te elementy które zawierają określoną frazę
:first - pierwszy ze znalezionych elementów
:last - ostatni ze znalezionych elementów


4. Niektóre z metod jQuery
Praca ze znalezionymi elementami strony jest łatwa dzięki licznym metodą jQuery. Niektóre z nich:
addClass("nazwa klasy") - nadaje klasę z pliku css
removeClass("klasa") - usuwa klasę
toggleClass("nowa_klasa") - zamienia aktualną klasę na nowa_klasa
attr("klucz", "wartosc") - dodaje atrybut klucz o wartości wartosc
prev() - zwraca element na tym samym poziomie zagnieżdżenia, znajdujący się przed wyselekcjonowanym elementem
next() - to samo co powyżej tylko że zwraca element za aktualnie wyselekcjonowanym
replaceAll(elment) - przepisuje zawartość wyszukanych elementów do obiektów określonych przez element np. $("p").replaceAll("div");
replaceWith(zawartosc) - przypisuje wyszukanym elementom treść zmiennej zawartosc
wrap(html) - umieszcza każdy znaleziony element pomiędzy znacznikami html
wrapAll(html) - wszystkie znalezione elementy umieszcza w pojedynczym znaczniku html
remove() - usuwa wszystkie elementy ze strony
width() - zwraca szerokość wybranych elementów
height() - zwraca wysokość wybranych elementów


5. Zdarzenia
Zdarzenia są niezwykle proste w obsłudze w jQuery. Zobaczmy to na przykładzie:
        $(function () {
            $(".link").click(function () {
                $(this).css("color", "Red");
            });
        });

Słówko this - identyfikuje element który został kliknięty. Inne metody które można wykorzystać:
hover(fponad, fpoza) - w momencie najechania na element wywoływana jest metoda fponad, gdy znów kursor zostanie cofnięty fpoza
click(f), keypress(f) - kliknięcie lub naciśnięcie przycisu
mouseout(f), mouseover(f), mouseenter(f), mouseleave(f) - funkcje wywoływane kolejno: w momencie usunięcia kursora z elementu, pojawienia się nad elementem
toggle(f1,f2,f3,...) - dla kolejnych kliknięć wywołuje funkcje f1, f2...
each(f) - dla każdego elementu wywołuje funkcję f
resize(f) - wywołuje funkcję gdy element zostanie przeskalowany


6. Efekty
jQuery umożliwia wykorzystanie efektów bez potrzeby tworzenia skomplikowanego kodu. Do wykorzystania mamy metody takie jak:
show(speed), hide(speed) - wyświetlanie i ukrywanie elementów, speed - czas w ms ile ma zająć efekt
fadeIn(speed), fadeOut(speed) - rozświetla lub wygasza dany element
fadeTo(speed, opacity) - animuje przeźroczystość elementu od aktualnego do opacity
slideIn(speed), slideOut(speed) - efekt wjazdu lub chowania
toggle() - przełącza pomiędzy wyświetlaniem i ukrywaniem elementów


7. Animacje
Animacje można bardzo prosto tworzyć za pomocą metody animate. Metoda ta przyjmuje dwa parametry: params (kod zgodny z css), oraz duration - czyli czas trwania animacji.

Jeżeli chcemy zatrzymać animację wystarczy skorzystać z metody stop().




Znając powyższe pojęcia możemy przejść do pisania znacznie bardziej skomplikowanych przykładów. Warto pamiętać, że każda metoda jQuery zwraca obiekt jQuery który można ponownie wykorzystać. Zobaczmy na przykład:
        $(function () {
            $("a").mouseover(function () { $(this).addClass("color", "Red") })
        .filter(":even").addClass("klasa").end()
        .filter(":odd").addClass("klasa2").end()
            //......
        });

Szukamy elementu a, reagujemy na zdarzenie mouseover. Znalezione elementy filtrujemy w poszukiwaniu nieparzystych elementów. Jeżeli znaleziono np. 10 elementów o po działaniu filtra otrzymamy 5. Funkcja end() mówi: "usuń działanie ostatniego filtra". Dzięki temu znów mamy 10 elementów do dyspozycji itd.


Oprócz standardowych elementów, na stronie http://jqueryui.com/ możemy znaleźć wiele gotowych efektów do wykorzystania w naszej aplikacji. Oprócz tego istnieje mnóstwo pluginów gotowych do wykorzystania w naszych projektach.


Źródło:
Scott Allen - jQuery for the ASP.NET Developer
http://jquery.com/
Komputer Świat Ekspert 5/2009

wtorek, 17 sierpnia 2010

ASP.NET MVC 2 cz. 8 Metodologia TDD

TDD Test-driven development - jedna z zwinnych metodyk (Agile) wytwarzania oprogramowania. Całą koncepcję TDD możemy przedstawić na diagramie:


Tak więc schemat jest prosty:
1. Programista chce dodać nową funkcję - pisze więc wpierw test
2. Wywołuje test który powinien zakończyć się niepowodzeniem
3. Programista implementuje żądaną funkcjonalność
4. Test w tym momencie powinien się powieść
5. Refaktoryzacja kodu


Jak to przekłada się na nasz projekt MVC?
Na początek musimy dysponować odpowiednimi narzędziami do testowania. Użyjemy dwóch popularnych narzędzi:
1. NUnit - strona domowa
2. Moq - strona domowa
Po pobraniu i zainstalowaniu tych narzędzi w naszej solucji tworzymy nowy projekt typu Class Library.
Teraz należy dodać odpowiednie referencje:
nunit.framework
system.web
system.web.Abstractions
system.web.Routing
system.web.Mvc
Moq.dll - z lokalizacji w której znajduje się zainstalowany Moq
Na koniec referencję do projektu Mvc oraz do innych wykorzystywanych projektów (np. Domain Model itp.)

Parę słów jeszcze o Mokowaniu:
Moq pozwala na tworzenie obiektów o dużej ilości zależności jak np. Response, Request itp. Przykłady zobaczymy w dalszej części.

Schemat testów najlepiej przedstawić jako model A/A/A czyli arrange/act/assert
arrange - tworzymy instancje potrzebnych klas np. kontrolera.
act - wywołujemy jakąś akcję, przekazujemy jej żądane parametry i gromadzimy wyniki
assert - upewniamy się że zwrócone rezultaty są zgodne z oczekiwanymi

Najpierw zobaczymy najprostsze przykłady testów:

1. Test na to czy kontroler zwraca poprawny widok:
A więc zaczynamy. W naszej aplikacji chcemy mieć możliwość wyświetlania produktów i szczegółowych informacji o nich. Piszemy więc test:
    [TestFixture]
    public class ProductControllerTest
    {
        [Test]
        public void Can_Display_Details_View()
        {
            //Arrange
            ProductController productController = new ProductController();
            //Act
            var result = (ViewResult)productController.Details(10);
            //Assert
            Assert.AreEqual("Details", result.ViewName);
        }
    }

Uruchamiamy NUnit i otrzymujemy wynik negatywny. Czas więc na implementację kontrolera i właściwej metody:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace Projekt.Controllers
{
    public class ProductController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }


        public ActionResult Details(int id)
        {
            return View("Details");
        }
    }
}


Po ponownym uruchomieniu testu powinniśmy otrzymać wynik pozytywny.


2. Testowanie zwracanych danych przez kontroler (ViewData)
Kolejny bardzo prosty typ testu to sprawdzenie czy to co przenosi w sobie ViewDate jest tym co oczekujemy.
Mamy więc sytuację: Do metody Details przekazujemy id = 10 a w odpowiedzi chcemy otrzymać informację że jest to komputer:
        [Test]
        public void Accion_Returns_Corret_Model()
        {
            ProductController productController = new ProductController();

            var result = (Product)((ViewResult)productController.Details(12)).ViewData.Model;

            Assert.AreEqual("komputer", result.Name);
        }

Po uruchomieniu NUnit otrzymamy błąd. Przechodzimy więc do implementacji akcji:
        public ActionResult Details(int id)
        {
            Product p = new Product
            {
                Id = 12,
                Name = "komputer"
            };

            return View("Details", p);
        }


3. Testowanie różnych typów zwracanych przez kontroler
Akacja może oczywiście zwrócić nie tylko widok ale również np. przekierowanie do innej strony. Zobaczmy to w praktyce:
        [Test]
        public void Accion_Redirect_ToIndex_When_Bad_Id()
        {
            ProductController productController = new ProductController();

            var result = (RedirectToRouteResult)productController.Details(-5);

            Assert.AreEqual("Index", result.RouteValues["action"].ToString());
        }

Test oczywiście się nie powiedzie więc implementujemy rozwiązanie:
        public ActionResult Details(int id)
        {
            if (id < 0)
            {
                return RedirectToAction("Index");
            }

            Product p = new Product
            {
                Id = 12,
                Name = "komputer"
            };

            return View("Details", p);
        }


Przedstawione przykładu pokazują jedynie najprostsze z możliwych testów jakie można wykonać. Bardziej złożone testy wymagają mokowania. Jest to wymagane np. w sytuacjach gdy korzystamy z Dependency Injection czy złożonych obiektów.


Kilka bardziej skomplikowanych przypadków testowych:
1. Testowanie tras routingu:
Stworzymy najpierw klasę z metodą pomocniczą, która dostarczy testowy obiekt klasy HttpContextBase:
    public class TestHelpers
    {
        public static Mock<HttpContextBase> MakeMockHttpContext(string url)
        {
            var mockHttpContext = new Mock<HttpContextBase>();
            var mockRequest = new Mock<HttpRequestBase>(); //Request
            mockHttpContext.Setup(x => x.Request).Returns(mockRequest.Object);
            mockRequest.Setup(x => x.AppRelativeCurrentExecutionFilePath).Returns(url);
            var mockResponse = new Mock<HttpResponseBase>();//Response
            mockHttpContext.Setup(x => x.Response).Returns(mockResponse.Object);
            mockResponse.Setup(x => x.ApplyAppPathModifier(It.IsAny<string>()))
            .Returns<string>(x => x);
            return mockHttpContext;
        }
    }

Następnie implementujemy właściwy test:
    [TestFixture]
    public class RoutingTests
    {
        [Test]
        public void ForwardSlashGoesToHomeIndex()
        {
            RouteCollection routeConfig = new RouteCollection();
            MvcApplication.RegisterRoutes(routeConfig);
            var mockHttpContext = TestHelpers.MakeMockHttpContext("~/");

            RouteData routeData = routeConfig.GetRouteData(mockHttpContext.Object);

            Assert.IsNotNull(routeData, "RouteData - null!!");
            Assert.IsNotNull(routeData.Route, "Nie znaleziono pasującej trasy");
            Assert.AreEqual("Home", routeData.Values["controller"], "Zły kontroler");
            Assert.AreEqual("Index", routeData.Values["action"], "Zła akcja");
        }
    }


2. Testowanie kontrolerów
Widzieliśmy wcześniej jak testować:
- poprawność zwracanych widoków
- dane przesyłane przez ViewData
- testowaliśmy przekierowania
Teraz coś trudniejszego: w akcji wykorzystamy inne obiekty jak Request, Response, Cookie:
        public ViewResult Homepage()
        {
            if (Request.Cookies["HasVisitedBefore"] == null)
            {
                ViewData["IsFirstVisit"] = true;
                Response.Cookies.Add(new HttpCookie("HasVisitedBefore", bool.TrueString));
            }
            else
                ViewData["IsFirstVisit"] = false;
            return View();
        }

Dla potrzeb testu stworzymy klasę pomocniczą która ustawi odpowiednie wartości potrzebnych nam obiektów:
        //źródło: Apress Pro ASP.NET MVC 2 Framework
        public class ContextMocks
        {
            public Moq.Mock<HttpContextBase> HttpContext { get; private set; }
            public Moq.Mock<HttpRequestBase> Request { get; private set; }
            public Moq.Mock<HttpResponseBase> Response { get; private set; }
            public RouteData RouteData { get; private set; }
            public ContextMocks(Controller onController)
            {
                HttpContext = new Moq.Mock<HttpContextBase>();
                Request = new Moq.Mock<HttpRequestBase>();
                Response = new Moq.Mock<HttpResponseBase>();
                HttpContext.Setup(x => x.Request).Returns(Request.Object);
                HttpContext.Setup(x => x.Response).Returns(Response.Object);
                HttpContext.Setup(x => x.Session).Returns(new FakeSessionState());

                Request.Setup(x => x.Cookies).Returns(new HttpCookieCollection());
                Response.Setup(x => x.Cookies).Returns(new HttpCookieCollection());
                Request.Setup(x => x.QueryString).Returns(new NameValueCollection());
                Request.Setup(x => x.Form).Returns(new NameValueCollection());
                RequestContext rc = new RequestContext(HttpContext.Object, new RouteData());
                onController.ControllerContext = new ControllerContext(rc, onController);
            }

            private class FakeSessionState : HttpSessionStateBase
            {
                Dictionary<string, object> items = new Dictionary<string, object>();
                public override object this[string name]
                {
                    get { return items.ContainsKey(name) ? items[name] : null; }
                    set { items[name] = value; }
                }
            }

Teraz nasz test możemy zapisać jako:
        [Test]
        public void Homepage_Recognizes_New_Visitor_And_Sets_Cookie()
        {
            var controller = new SimpleController();
            var mocks = new ProjektTests.TestHelpers.ContextMocks(controller);

            ViewResult result = controller.Homepage();

            Assert.IsEmpty(result.ViewName);
            Assert.IsTrue((bool)result.ViewData["IsFirstVisit"]);
            Assert.AreEqual(1, controller.Response.Cookies.Count);
            Assert.AreEqual(bool.TrueString,
            controller.Response.Cookies["HasVisitedBefore"].Value);
        }

Tak w skrócie przedstawia się sprawa testowania metod. Pamiętajcie najpierw test - potem implementacja.