Tymczasem w projekcie... Na razie klient może sobie dodać swój numer telefonu do obiektu mediów. Chciałbym teraz, żeby każdy z klientów mógł zasubskrybować medium i w dalszej perspektywie medium mogło informować klientów o egzemplarzach swoich periodyków. Jak zawsze, gdy mamy fazę green procesu TDD piszemy test jednostkowy:
Od razu jestem na czerwono:
Wchodzę w fazę red procesu TDD i piszę kod produkcyjny. I tak w kółko. Test Driven Development jest dyscypliną. Nie musi być mądre to, co robię, ważne żeby przyniosło oczekiwany efekt. Porównując to do koksika ćwiczącego na siłowni - podnoszenie 1000 razy tego samego ciężaru, jako takie, jest głupie, nic nie wnoszące. Ale z punktu widzenia rozwoju mięśni - takie ciągłe, zdawałoby się nieefektywne działanie, w dłuższej perspektywie prowadzi do rozwoju bica i trica.
No więc, żeby ten test przechodził - napisałem następujący kod produkcyjny:
Szybkie uruchomienie rake wskazuje, że jestem w fazie green.
Zielono, więc piszę test, który sprawdza czy mogę dodać aktualnego klienta do listy subskrybentów w klasie mediów.
Czerwono i do kodu produkcyjnego:
Dooobra i zielono, ale chciałbym mieć w tej tablicy subscribers tylko takie obiekty, które implementują interfejs Publicable zawierający metodę Publish.
A więc najpierw test. Gdy do metody add_to_subscribers wpadnie coś, co nie implementuje Publicable poproszę o wyjątek:
Blok assert_raises sprawdza, czy w danym bloku kodu - pomiędzy do i end poleciał wyjątek. Spodziewam się, że nie będzie można dodać liczby 125 do tej kolekcji.
I rzeczywiście poleciał wyjątek (inny), bo nie poleciał wyjątek (w kodzie produkcyjnym):
Jestem w fazie czerwonej, więc zawijam kiecę i lecę do kodu produkcyjnego:
Metoda .instance_of? {nazwa_klasy} sprawdza, czy dany obiekt jest danego typu. Ok, nadal mam error - tym razem kompilacji, bo nie ma takiego bytu jak Publicable. Tworzymy odpowiedni interfejs i importujemy go.
Ale zaraz zaraz Ruby podobno nie ma w sobie interfejsów!
Jak to?
Okazuje się, że rzeczywiście nie ma interfejsów w Ruby, ponieważ Ruby jest językiem dynamicznie typowanym. W Ruby rozpoznajemy typ obiektu nie na podstawie deklaracji typu, ale poprzez badanie metod na tym obiekcie. To tak zwane kacze typowanie. Nazwa ta pochodzi od powiedzenia, że "Jeżeli coś chodzi jak kaczka i kwacze jak kaczka, to musi to być kaczką". Hmmm, ciekawe.
Ok, czyli moje sprawdzenie czy client jest obiektem pewnej klasy, zamieniam na sprawdzenie czy client ma konkretne metody. Dzieje się to przy użyciu:
.respond_to? :nazwa_metody
Czyli jeśli nie posiadasz metody publish to rzuć wyjątkiem.
Odpalam testy i okazuje się, że poprzedni test przestał działać:
A to dlatego, że rzeczywiście klasa ItPeople nie ma metody publish. Jeszcze nie ma ;)
No i jestem na zielono. Jestem tym zaskoczony, że w języku dynamicznym tak mało mam do czynienia z konkretnymi typami. Wydaje mi się, że taki język jest wygodniejszy do testowania jednostkowego. A już, że wyjątek rzuciłem przy użyciu słówka kluczowego raise i opisowego stringa to jest bajka :) Ciekawe czy dynamiczne typowanie doprowadzi mnie do jakiejś katastrofy, czy nadal będę mógł jechać na autopilocie testów jednostkowych, mając zawiązane oczy na konkretne typy.
Kommit do tego TU.
def test_if_media_have_subscibers_structure assert_not_nil @media.subscribers end
Od razu jestem na czerwono:
...E ========================================================================================================== Error: test_if_media_have_subscibers_structure(SubscriptionTests): NoMethodError: undefined method `subscr ibers'
Wchodzę w fazę red procesu TDD i piszę kod produkcyjny. I tak w kółko. Test Driven Development jest dyscypliną. Nie musi być mądre to, co robię, ważne żeby przyniosło oczekiwany efekt. Porównując to do koksika ćwiczącego na siłowni - podnoszenie 1000 razy tego samego ciężaru, jako takie, jest głupie, nic nie wnoszące. Ale z punktu widzenia rozwoju mięśni - takie ciągłe, zdawałoby się nieefektywne działanie, w dłuższej perspektywie prowadzi do rozwoju bica i trica.
No więc, żeby ten test przechodził - napisałem następujący kod produkcyjny:
class Media attr_accessor :ads, :subscribers def initialize @ads = Array.new @subscribers = Array.new end def add_advertising telephone_number @ads.push telephone_number end end
Szybkie uruchomienie rake wskazuje, że jestem w fazie green.
Started .... Finished in 0.000670749 seconds. 4 tests, 8 assertions, 0 failures, 0 errors, 0 pendings, 0 omissions, 0 notifications--------------------- 100% passed
Zielono, więc piszę test, który sprawdza czy mogę dodać aktualnego klienta do listy subskrybentów w klasie mediów.
def test_if_client_can_be_added_to_subscribers_list it_people = ItPeople.new @media.add_to_subscibers it_people end
Czerwono i do kodu produkcyjnego:
def add_to_subscribers client @subscribers.push client end
Dooobra i zielono, ale chciałbym mieć w tej tablicy subscribers tylko takie obiekty, które implementują interfejs Publicable zawierający metodę Publish.
A więc najpierw test. Gdy do metody add_to_subscribers wpadnie coś, co nie implementuje Publicable poproszę o wyjątek:
def test_if_there_an_exception_if_there_was_no_publicable_implementation_on_entry assert_raises do @media.add_to_subscribers 125 end end
Blok assert_raises sprawdza, czy w danym bloku kodu - pomiędzy do i end poleciał wyjątek. Spodziewam się, że nie będzie można dodać liczby 125 do tej kolekcji.
I rzeczywiście poleciał wyjątek (inny), bo nie poleciał wyjątek (w kodzie produkcyjnym):
/usr/share/ruby-2.3.1/bin/ruby ./test/all_tests.rb Loaded suite ./test/all_tests Started .....F ========================================================================================================== Failure: <[]> exception expected but none was thrown. test_if_there_an_exception_if_there_was_no_publicable_implementation_on_entry(SubscriptionTests) /home/coola/dsp2017/xp-simulator/test/subscription_tests.rb:37:in `test_if_there_an_exception_if_there_was _no_publicable_implementation_on_entry' 34: 35: def test_if_there_an_exception_if_there_was_no_publicable_implementation_on_entry 36: => 37: assert_raises do 38: @media.add_to_subscribers 125 39: end 40: end ==========================================================================================================
Jestem w fazie czerwonej, więc zawijam kiecę i lecę do kodu produkcyjnego:
def add_to_subscribers client if client.instance_of? Publicable @subscribers.push client end end
Metoda .instance_of? {nazwa_klasy} sprawdza, czy dany obiekt jest danego typu. Ok, nadal mam error - tym razem kompilacji, bo nie ma takiego bytu jak Publicable. Tworzymy odpowiedni interfejs i importujemy go.
Ale zaraz zaraz Ruby podobno nie ma w sobie interfejsów!
Jak to?
Okazuje się, że rzeczywiście nie ma interfejsów w Ruby, ponieważ Ruby jest językiem dynamicznie typowanym. W Ruby rozpoznajemy typ obiektu nie na podstawie deklaracji typu, ale poprzez badanie metod na tym obiekcie. To tak zwane kacze typowanie. Nazwa ta pochodzi od powiedzenia, że "Jeżeli coś chodzi jak kaczka i kwacze jak kaczka, to musi to być kaczką". Hmmm, ciekawe.
Ok, czyli moje sprawdzenie czy client jest obiektem pewnej klasy, zamieniam na sprawdzenie czy client ma konkretne metody. Dzieje się to przy użyciu:
.respond_to? :nazwa_metody
def add_to_subscribers client if !client.respond_to? :publish raise 'Argument should have publish method' end @subscribers.push client end
Czyli jeśli nie posiadasz metody publish to rzuć wyjątkiem.
Odpalam testy i okazuje się, że poprzedni test przestał działać:
Started ..E ========================================================================================================== Error: test_if_client_can_be_added_to_subscribers_list(SubscriptionTests): RuntimeError: Argument should h ave publish method /home/coola/dsp2017/xp-simulator/src/Media.rb:17:in `add_to_subscribers' /home/coola/dsp2017/xp-simulator/test/subscription_tests.rb:31:in `test_if_client_can_be_added_to_subscrib ers_list' 28: 29: def test_if_client_can_be_added_to_subscribers_list 30: => 31: @media.add_to_subscribers @it_people 32: 33: end 34:
A to dlatego, że rzeczywiście klasa ItPeople nie ma metody publish. Jeszcze nie ma ;)
class ItPeople attr_accessor :telephone_number def publish end end
No i jestem na zielono. Jestem tym zaskoczony, że w języku dynamicznym tak mało mam do czynienia z konkretnymi typami. Wydaje mi się, że taki język jest wygodniejszy do testowania jednostkowego. A już, że wyjątek rzuciłem przy użyciu słówka kluczowego raise i opisowego stringa to jest bajka :) Ciekawe czy dynamiczne typowanie doprowadzi mnie do jakiejś katastrofy, czy nadal będę mógł jechać na autopilocie testów jednostkowych, mając zawiązane oczy na konkretne typy.
Kommit do tego TU.
Komentarze
Prześlij komentarz