Global Azure Bootcamp we Wrocławiu! 2017-logo-200x135

Witajcie!

zapraszamy na piąty odcinek codingtv();

Dzisiaj zaczynamy tworzyć repozytorium – a dokładniej operacje na klasie Blog. Standardowo zaczynamy od testów – pokazujemy w jaki sposób wykorzystać w nich bazę SQL Compact Edition 4 (jako jedną z opcji – w następnych odcinkach pokażemy mockowanie contextu).

Jak zapowiadaliśmy w pierwszym odcinku, staramy się do nagrań specjalnie nie przygotowywać, co spowodowało dzisiaj małe problemy przy testach – liczymy tutaj na Wasze komentarze 🙂

Tak jak poprzednio, dzisiejszy odcinek jest podzielony na 3 części, które zamieszczamy w postaci playlisty, dzięki czemu poszczególne części powinny się same przełączać.

Zapraszamy do oglądania i czekamy na Wasze komentarze!

Linki do wykorzystywanych pojęć/aplikacji:

25 komentarzy

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *

  • Gratuluje kolejnego odcinka. Napięcie stopniujecie powoli jak w dobrym horrorze 🙂
    Podnieśliście mnie na duchu, gdyż moje pierwsze spotkanie z TDD rozpoczęło się i skończyło tak jak u Was => głowa pełna teorii i ostre zderzenie z rzeczywistością 🙂 Sam chętnie dowiem się jak dobrze przetestować repozytorium.

    PS. Pamiętajcie o zasadzie F.I.R.S.T.

  • Witam, przede wszystkim świetna robota i gratulacje za bloga, myślę że nieustanne motywowanie Was jest wskazane. Wiem ile pracy zajmuje pisanie posta na blogu, a co dopiero prowadzenie takiego „programu” 🙂

    Co do testów bez R#, ja również używam Visual NUnit i nigdy nie spotkałem się z tym, aby nie działał prawidłowo. Dwa razy miałem tak, że przy błędzie w kodzie (z mojego powodu) zwracał inny exception, niż bezpośrednie uruchomienie przez NUnit. Jednak jak ktoś nadal jest sceptycznie nastawiony do Visual Nunit, polecam wpis Maćka : http://www.maciejaniserowicz.com/post/2008/02/27/Visual-Studio-Express-NUnit.aspx

    Co prawda w VS2010 jest trochę inaczej niż na blogu Maćka, ale myślę, że wszyscy powinni dać sobie radę.

    Co do testów napisałem o tym na swoim blogu, taka mała reklama :P, choć nie wiem czy Wam się to przyda.

  • Testy – gdy naszym celem jest przetestowanie interakcji z bazą danych. Wydaje mi się że nie warto stosować mock’ow, choć w pewnym sensie Sql Server CE można za takiego uznać 😉 Dla mnie w przypadku takich testów interesujące jest faktycznie działanie EF z bazą i naszym repozytorium. Polecam użyć mechanizmu transakcji, u nas w firmie z powodzeniem używamy rollback do tworzenia niezależnych testów na jednej bazie 🙂

    PS.
    Wydaje mi się, że kiedy wywołujemy na obiekcie jakiekolwiek operacje, to zbytecznym jest testowanie typu Assert.NotNull, ponieważ samo wywołanie operacji (property) na danym obiekcie obsłuży nam ten przypadek 🙂

    Jak zwykle kawał dobrej roboty. Ciekawy pomysł z makrem, zastanawiające czy dużo pracy zajeło by dopisanie prostego extension do VS lub R# (quick fix ?) do zrealizowanie tej samej czynności 🙂

    • Jeśli chodzi o Assert.NotNull na obiekcie blog – faktycznie kłóci się to z zasadą wykonywania jednej asercji w teście. Jednak w rezultacie testu wg mnie na pierwszy rzut oka łatwiej będzie rozpoznać błąd w asercji not null niż NullRefException. I tu pojawia się kolejne „ale” – przecież skoro chcemy sprawdzić czy blog rzeczywiście jest „not null” po operacji zapisu, powinniśmy użyć osobnej metody testowej do tego przeznaczonej. Czy w takim razie testując zapis obiektu z dwoma propertami powinniśmy stworzyć dwa testy (jeden dla każdej property) czy jeden dla obu propert? Roy Osherove zaleca właśnie to pierwsze podejście. Wówczas w wynikach testów zawsze będzie dokładnie widoczne czy zapis nie powiódł się dla jednej czy też może większej ilości propert (co nie jest możliwe jeśli wszystkie asercje są w jednym teście).

    • @jacek.spolnik:
      Zgadzam się z Andrzejem, Assert.NotNull od razu komunikuje z czym jest problem, natomiast NullReferenceException może być gdziekolwiek w ciele metody na którą wskazuje stack trace i bez debugowania się nie obejdziesz.

      @Andrzej:
      Roy pisze, że wiele Assert’ów jest akceptowalne jeśli spełniony jest warunek:
      gdy jeden Assert nie jest spełniony, to czy nadal zależy Ci na pozostalych, czy już wtedy nie?
      Gdy sprawdzamy kilka property tego samego obiektu to raczej nie ma znaczenia czy pozostałe Assert’y są spełnione.

  • Witajcie i gratuluję! Kolejny udany odcinek 😉 W tym odcinku doszło do lekkiego zamieszania i całkiem dobrze, ponieważ można było zauważyć wymianę zdań pomiędzy wami i przede wszystkim sposób argumentowania przyjętych założeń. Chciałbym poruszyć kilka rzeczy o których wspomnieliście w trakcie tworzenia testów bloga.
    1. Moim zdaniem Artur miał rację, że podczas testowania zapisu encji „Blog” do bazy należy stosować zasadę jeden test na jedną funkcjonalność. W związku z tym sprawdzanie tego co było w poprzednim teście narusza tą zasadę. Moim zdaniem testowanie zapisu encji „Blog” powinno przebiegać w 3 etapach:
    I. wypełnienie modelu danymi (pewnie w późniejszych odcinkach zostaną dodane powiązane encje w postaci list czy zwykłych właściwości)
    II. zapis do bazy
    III. odczyt danych po id
    IV. porównanie identyczności właściwości na modelu oraz powiązanych encji.
    Sprawdzanie stanu bazy danych po liczebności encji Blog jest warunkiem koniecznym ale nie wystarczającym na sprawdzenie persystentności waszego repozytorium.
    2. Co do podejścia tworzenia czystej instancji bazy na starcie każdego testu, to wyobraźcie jak by miały wyglądać testy repozytorium dajmy na to encji Blog, które miałby 100 metod testowych? Samo utworzenie bazy przy SetUp-ie metody opóźnia wykonanie jednego testu o jakieś 5 sek, co przy takiej ilości testów zniechęca w ogóle do ich uruchamiania. Testy powinny wykonywać się szybko. Stąd też moja propozycja, żeby inicjować bazę raz przy startupie na klasie, a na każdym SetUp-ie i TearDownie metody testowej otwierać transakcję i ją cofać. Takie rozwiązanie jest o wiele szybsze niż odtwarzanie bazy per test, a co więcej pozostawia spójny stan bazy (czyli pusty) po zakończeniu testu.
    3. I jeszcze takie czepialskie: jak macie repozytorium BlogRepository i tworzycie metodę o wdzięcznej nazwie CreateBlog, to czy nie jest to za wiele szczęścia na raz ? Przecież wiadomo, że będąc w kontekście tego repozytorium domyślnie chodzi o operacje na encji Blog. Poza tym zdziwiłem się, że jak generowaliście namiastkę repozytorium, to ręcznie dodaliście klasę w projekcie. Osobiście korzystając z R# użyłbym w kontekście tamtej nazwy klasy Alt+Enter i wybrał „Create class …”, a potem przeniósł do osobnego pliku i do projektu z repozytoriami.

    • Ad 2.
      Należy jednak pamiętać, że nawet jak zrobimy rollback to w bazie może zostać ślad. Nie pamiętam jak chłopaki robili klucz główny ale gdy mamy autoincrement to po rollback zostaje on i tak zwiększony. Nie jest to kluczowa sprawa ale trzeba o tym pamiętać gdyby się chciało wykorzystać to w asercjach.

    • Ad 2. Jak powiedziałem w nagraniu zdajemy sobie sprawę z kiepskiej wydajności naszego pierwszego pomysłu i nie będziemy się przy nim upierać. Zastosujemy transkacje.

      Ad 3. Wg mnie instrukcja new BlogRepository().Create() może mylnie kojarzyć się z tworzeniem nowego repozytorium. Po chwili zastanowienia może to być jasne, ale chyba dodanie słowa Blog do nazwy metody nie jest tu problemem, a wręcz zwiększa (wg mnie) czytelność kodu.
      Jeśli chodzi automatyczne tworzenie klas – stare przyzwyczajenia robią swoje, ale staram się wykorzystywać coraz szersze spektrum możliwości R# 🙂

    • Dokładnie, po zapisie do bazy należy odczytać po ID i porównać obiekty (wejściowy i wyjściowy).
      Natomiast sprawdzanie Count’ów to zły pomysł i prowadzi w ślepy zaułek.

  • Witam,
    Jak widać, każda pora jest dobra aby oglądnąć kolejny odcinek Waszego wspaniałego cyklu.

    Nurtuje mnie kilka pytań:
    1. Czy jest sens testowania czy blog jest null (w metodzie should_be_able…) skoro nigdy nie będzie? Zwracany jest przecież ten sam obiekt niezależnie od tego czy się zapisze do bazy.

    2. Czy jest sens testowania, czy nie ma wpisów w bazie skoro w setupie ustawione jest AlwaysDrop?

    3. Ponieważ tematyka testów jest mi znana równie dobrze jak poczet carów Rosji zrobiłem pierwszy krok „przerabiając” http://www.viddler.com/explore/RoyOsherove/videos/25/ . Na podstawie świeżo zdobytej wiedzy zastanawiam się czy nie lepiej umieścić jeden wspólny kontekst dla wszystkich testów i utworzyć helpera który sprzątałby sprawnie baze (jakiś szybki trucate tabel)?

    • Ad 1. Wydaje mi się, że test nie powinien nic zakładać o implementacji danej metody. Chcemy być pewni że zapis do bazy zwróci obiekt zapisany, nigdy null, a o błędzie poinformuje nas wyjątkiem (bądź odpowiednio zareaguje jeśli testowana metoda np. ma wykonać walidację). I takie właśnie warunki ustawiamy w asercji. Generalnie test ma służyć nam na przyszłość i zapewnić, że to co zmienimy w kodzie później nie spowoduje kataklizmu 🙂

      Ad 2. Faktycznie ten pierwszy test służy do przetestowania logiki metody Setup klasy testowej. Postaramy się to sprawnie posprzątać.

  • Wydaje mi się, że usuwanie i ponowne tworzenie bazy przed każdym testem to bardzo zły pomysł (z czasem będą potrzebne dane testowe więc sam mechanizm EF do tworzenia bazy nie będzie wystarczający). Wydaje mi się, że najlepiej będzie zostawić tworzenie bazy w metodzie TestFixtureSetUp a dodać dwie metody:
    – SetUp – gdzie będziecie zaczynać nową transakcję
    – TearDown – gdzie transakcja będzie rolback’owana.
    Dzieki temu każdy test będzie wykonywany na bazie znajdującej się w takim samym stanie.

    A tak ogólnie to świetna robota:) Już nie mogę się doczekać kolejnego odcinka.

    • Podoba mi się to rozwiązanie z transakcjami. Co do danych testowych – można zdefiniować stan początkowy bazy korzystając z EF definiując własny inicjator dziedziczący po jednym z dwóch istniejących. Mam nadzieję że to także pokażemy w jednym z najbliższych odcinków.

  • Moja propozycja to użyć SQLite.
    Jako, że zaczynam się uczyć ASP.NET MVC zainspirowany Waszym projektem tworzę na boku swój własny „CodingBlog” tyle, że z wykorzystaniem NHibernate. Do kilku testów sprawdzających komunikację z bazą używam właśnie SQLite w trybie „in memory”. Prędkość wykonywania takich testów jest naprawdę zadowalająca. Zastanówcie się czy czasem nie przesiąść się na to rozwiązanie.
    Mam nadzieję, że bazę będziecie wykorzystywać tylko do kilku testów a potem przesiądziecie się na Moq.

  • Testowanie kontekstu z wykorzystaniem bazy Sql Server Compact mija się z celem. Chyba po to mamy już zbudowaną bazę, żeby nie tworzyć kolejnej. Lepiej chyba działać na „żywym organizmie” w połączeniu z transakcjami jak to już zostało wcześniej napisane:)

  • Czy przypadkiem otworzenie transakcji w Setup i jej rollback w TearDown nie spowoduje, że po zapisaniu danych do bazy w metodzie testowej nie będziemy mogli ich odczytać w tej samej metodzie(isolation level read commited)?

  • Wogóle to zastanawiam się nad tą ideą wykorzystania SQL Ce. Nie rozumiem przewagi tego na wykorzystaniem bazy testowej na normalnym SQL Serverze. Nie jest szybsze. Poza tym i tak musicie zrobić testy na prawdziwym serwerze.

    Staracie się zrobić testy jednostkowe chociaż tutaj mamy do czynienia z testami integracji.

  • Powiedzcie jak zrobiliście żeby VS był czarny. Wiem, że jest dodatek od MS, który pozwala na zmiany kolorów. Ale on chyba nie potrafił zmieniać menu kontekstowych. Czy po prostu zmieniliście ustawienia w Windowsie? Może udostępnicie schemat kolorów VS (samego IDE nie edytora kodu).