Tytuł oryginału: Test-Driven Development with Python · 2020. 11. 10. · Tytuł oryginału:...

44

Transcript of Tytuł oryginału: Test-Driven Development with Python · 2020. 11. 10. · Tytuł oryginału:...

  • Tytuł oryginału: Test-Driven Development with Python

    Tłumaczenie: Robert Górczyński

    ISBN: 978-83-283-1377-4

    © 2015 Helion S.A.

    Authorized Polish translation of the English edition of Test-Driven Development with Python, ISBN: 9781449364823 © 2014 Harry Percival.

    This translation is published and sold by permission of O’Reilly Media, Inc., which owns or controls all rights to publish and sell the same.

    Polish edition copyright © 2015 by Helion S.A. All rights reserved.

    All rights reserved. No part of this book may be reproduced or transmitted in any form or by any means, electronic or mechanical, including photocopying, recording or by any information storage retrieval system, without permission from the Publisher.

    Wszelkie prawa zastrzeżone. Nieautoryzowane rozpowszechnianie całości lub fragmentu niniejszej publikacji w jakiejkolwiek postaci jest zabronione. Wykonywanie kopii metodą kserograficzną, fotograficzną, a także kopiowanie książki na nośniku filmowym, magnetycznym lub innym powoduje naruszenie praw autorskich niniejszej publikacji.

    Wszystkie znaki występujące w tekście są zastrzeżonymi znakami firmowymi bądź towarowymi ich właścicieli.

    Autor oraz Wydawnictwo HELION dołożyli wszelkich starań, by zawarte w tej książce informacje były kompletne i rzetelne. Nie biorą jednak żadnej odpowiedzialności ani za ich wykorzystanie, ani za związane z tym ewentualne naruszenie praw patentowych lub autorskich. Autor oraz Wydawnictwo HELION nie ponoszą również żadnej odpowiedzialności za ewentualne szkody wynikłe z wykorzystania informacji zawartych w książce.

    Wydawnictwo HELIONul. Kościuszki 1c, 44-100 GLIWICEtel. 32 231 22 19, 32 230 98 63e-mail: [email protected]: http://helion.pl (księgarnia internetowa, katalog książek)

    Pliki z przykładami omawianymi w książce można znaleźć pod adresem: ftp://ftp.helion.pl/przyklady/tddwpr.zip

    Drogi Czytelniku!Jeżeli chcesz ocenić tę książkę, zajrzyj pod adres http://helion.pl/user/opinie/tddwprMożesz tam wpisać swoje uwagi, spostrzeżenia, recenzję.

    Printed in Poland.

    • Kup książkę• Poleć książkę • Oceń książkę

    • Księgarnia internetowa• Lubię to! » Nasza społeczność

    ttp://helion.pl/rt/tddwprttp://helion.pl/rf/tddwprttp://helion.pl/ro/tddwprttp://helion.plhttp://ebookpoint.pl/r/4CAKF

  • 5

    Spis tre ci

    Wprowadzenie ............................................................................................................ 13 Przygotowania i za o enia ......................................................................................... 19 Podzi kowania ............................................................................................................25

    I Podstawy TDD i Django ..........................................................................27

    1. Konfiguracja Django za pomoc testu funkcjonalnego .............................................29S uchaj Testing Goat! Nie rób nic, dopóki nie przygotujesz testu ............................................. 29Rozpocz cie pracy z frameworkiem Django ................................................................................. 32Utworzenie repozytorium Git .......................................................................................................... 33

    2. Rozszerzenie testu funkcjonalnego za pomoc modu u unittest ............................. 37U ycie testu funkcjonalnego do przygotowania minimalnej aplikacji ..................................... 37Modu unittest ze standardowej biblioteki Pythona .................................................................... 40Ukryte oczekiwanie ............................................................................................................................ 42Przekazanie plików do repozytorium ............................................................................................ 42

    3. Testowanie prostej strony g ównej za pomoc testów jednostkowych ..................45Nasza pierwsza aplikacja Django i test jednostkowy .................................................................. 46Testy jednostkowe i ró nice dziel ce je od testów funkcjonalnych .......................................... 46Testy jednostkowe w Django ........................................................................................................... 47MVC w Django, adresy URL i funkcje widoku ............................................................................ 48Wreszcie zaczynamy tworzy kod aplikacji .................................................................................. 49urls.py ................................................................................................................................................... 51Testy jednostkowe widoku ............................................................................................................... 53

    Cykl test jednostkowy — tworzenie kodu ..............................................................................54

    4. Do czego s u te wszystkie testy? ............................................................................ 57Programowanie przypomina wyci ganie wiadrem wody ze studni ........................................ 58U ycie Selenium do testowania interakcji u ytkownika ............................................................ 59Regu a „nie testuj sta ych” i szablony na ratunek ........................................................................ 62

    Refaktoryzacja w celu u ycia szablonu ....................................................................................62

    Poleć książkęKup książkę

    http://helion.pl/rf/tddwprhttp://helion.pl/rt/tddwpr

  • 6 Spis tre ci

    Refaktoryzacja ..................................................................................................................................... 65Nieco wi cej o stronie g ównej ......................................................................................................... 67Przypomnienie — proces TDD ........................................................................................................ 68

    5. Zapis danych wej ciowych u ytkownika ................................................................... 73Od formularza sieciowego do wykonania dania POST .......................................................... 73Przetwarzanie dania POST w serwerze ..................................................................................... 76Przekazanie zmiennych Pythona do wygenerowania w szablonie .......................................... 77Do trzech razy sztuka, a pó niej refaktoryzacja ........................................................................... 81Django ORM i nasz pierwszy model .............................................................................................. 82

    Pierwsza migracja bazy danych ................................................................................................84Zdumiewaj co du y post p w te cie ........................................................................................85Nowa kolumna oznacza now migracj ..................................................................................85

    Zapis w bazie danych informacji z dania POST ....................................................................... 86Przekierowanie po wykonaniu dania POST .............................................................................. 89

    Poszczególne testy powinny testowa pojedyncze rzeczy ...................................................89Wygenerowanie elementów w szablonie ....................................................................................... 90Utworzenie produkcyjnej bazy danych za pomoc polecenia migrate .................................... 92

    6. Przygotowanie minimalnej dzia aj cej wersji witryny ............................................. 97Gwarancja izolacji testu w testach funkcjonalnych ...................................................................... 97

    Wykonanie tylko testów jednostkowych ...............................................................................100Stawiaj na ma e projekty ................................................................................................................. 101

    YAGNI! ........................................................................................................................................102REST ..............................................................................................................................................102

    Implementacja nowego projektu za pomoc TDD ..................................................................... 103Iteracja w kierunku nowego projektu ........................................................................................... 105Testowanie widoków, szablonów i adresów URL za pomoc testu klienta Django ........... 107

    Nowa klasa testowa ...................................................................................................................107Nowy adres URL ........................................................................................................................108Nowa funkcja widoku ...............................................................................................................108Oddzielny szablon do wy wietlania list ................................................................................109

    Kolejny adres URL i widok pozwalaj cy na dodanie elementów listy .................................. 112Klasa testowa dla operacji tworzenia nowej listy ................................................................112Adres URL i widok przeznaczony do tworzenia nowej listy ............................................113Usuni cie zb dnego kodu i dalsze testy ................................................................................114Wskazanie formularzy w nowym adresie URL ...................................................................115

    Dostosowanie modeli ....................................................................................................................... 116Zwi zek klucza zewn trznego ................................................................................................117Dostosowanie reszty wiata do naszych nowych modeli ..................................................118

    Ka da lista powinna mie w asny adres URL ............................................................................. 120Przechwytywanie parametrów z adresów URL ...................................................................121Dostosowanie new_list do nowego wiata ............................................................................122

    Jeszcze jeden widok pozwalaj cy na dodanie elementu do istniej cej listy .......................... 123Uwaga na ar oczne wyra enia regularne! ...........................................................................124Ostatni nowy adres URL ...........................................................................................................124

    Poleć książkęKup książkę

    http://helion.pl/rf/tddwprhttp://helion.pl/rt/tddwpr

  • Spis tre ci 7

    Ostatni nowy widok ..................................................................................................................125Jak mo na u y adresu URL w formularzu? ........................................................................126

    Ostatnia refaktoryzacja za pomoc polecenia include ............................................................... 128

    II Programowanie sieciowe ..................................................................... 131

    7. Upi kszanie — jak przetestowa uk ad i style? ....................................................... 133Jak funkcjonalno nale y testowa w przypadku uk adu i stylów? ................................... 133Upi kszanie za pomoc frameworka CSS .................................................................................... 136Dziedziczenie szablonu w Django ................................................................................................. 137Integracja z frameworkiem Bootstrap ........................................................................................... 139

    Wiersze i kolumny .....................................................................................................................139Pliki statyczne w Django ................................................................................................................. 140

    Zaczynamy u ywa klasy StaticLiveServerCase ..................................................................141U ycie komponentów Bootstrap do poprawy wygl du witryny ............................................ 142

    Jumbotron ....................................................................................................................................142Ogromne pola danych wej ciowych .......................................................................................143Nadanie stylu tabeli ...................................................................................................................143

    U ycie w asnych arkuszy stylów CSS .......................................................................................... 143Co zosta o zatuszowane — polecenie collectstatic i inne katalogi statyczne ........................ 144Kilka tematów, które nie zosta y omówione ............................................................................... 147

    8. TDD na przyk adzie witryny prowizorycznej ........................................................... 149Techniki TDD i niebezpiecze stwa zwi zane z wdro eniem .................................................. 150Jak zwykle zaczynamy od testu ..................................................................................................... 151Pobranie nazwy domeny ................................................................................................................. 153R czne przygotowanie serwera do hostingu naszej witryny ................................................... 153

    Wybór hostingu dla witryny ....................................................................................................154Uruchomienie serwera ..............................................................................................................154Konto u ytkownika, SSH i uprawnienia ...............................................................................155Instalacja Nginx ..........................................................................................................................155Konfiguracja domen dla witryn prowizorycznej i rzeczywistej ........................................156U ycie testów funkcjonalnych do potwierdzenia dzia ania domeny i serwera Nginx ....157

    R czne wdro enie kodu .................................................................................................................. 157Dostosowanie po o enia bazy danych ...................................................................................158Utworzenie virtualenv ..............................................................................................................159Prosta konfiguracja Nginx ........................................................................................................162Utworzenie bazy danych za pomoc polecenia migrate ....................................................164

    Wdro enie w rodowisku produkcyjnym ................................................................................... 164U ycie Gunicorn .........................................................................................................................164U ycie Nginx do obs ugi plików statycznych ......................................................................165U ycie gniazd systemu Unix ...................................................................................................166Przypisanie opcji DEBUG warto ci False i ustawienie ALLOWED_HOSTS ..................167U ycie Upstart do uruchamiania Gunicorn wraz z systemem ..........................................168Zachowanie wprowadzonych zmian — dodanie Gunicorn do pliku requirements.txt ....168

    Automatyzacja ................................................................................................................................... 169Zachowanie informacji o post pie ..........................................................................................172

    Poleć książkęKup książkę

    http://helion.pl/rf/tddwprhttp://helion.pl/rt/tddwpr

  • 8 Spis tre ci

    9. Zautomatyzowane wdro enie za pomoc Fabric .....................................................173Analiza skryptu Fabric dla naszego wdro enia .......................................................................... 174Wypróbowanie rozwi zania ........................................................................................................... 177

    Wdro enie w rodowisku produkcyjnym .............................................................................179Pliki konfiguracyjne Nginx i Gunicorn odtworzone za pomoc sed ...............................180

    U ycie polecenia git tag do oznaczenia wydania ....................................................................... 181Dalsza lektura .................................................................................................................................... 181

    10. Weryfikacja danych wej ciowych i organizacja testu ............................................. 183Testy funkcjonalne weryfikacji danych — ochrona przed pustymi elementami ................. 183

    Pomini cie testu ..........................................................................................................................184Podzia testów funkcjonalnych na wiele plików ..................................................................185Wykonanie pojedynczego pliku testu ....................................................................................187Podparcie testów funkcjonalnych ...........................................................................................188

    Sprawdzenie warstwy modelu ...................................................................................................... 189Refaktoryzacja testów jednostkowych na oddzielne pliki ..................................................189Testy jednostkowe sprawdzania modelu oraz mened er kontekstu self.assertRaises() ....190Dziwactwo Django — zapis modelu nie wywo uje operacji sprawdzenia poprawno ci ...191

    Wy wietlanie w widoku b dów z weryfikacji modelu ............................................................ 192Upewnienie si , e nieprawid owe dane nie zostan zapisane w bazie danych ...........194

    Wzorzec Django — przetwarzanie da POST w widoku generuj cym formularz ......... 196Refaktoryzacja — przekszta cenie funkcjonalno ci new_item na view_list ....................197Egzekwowanie w widoku view_list weryfikacji modelu ...................................................199

    Refaktoryzacja — usuni cie na sta e zdefiniowanych adresów URL ..................................... 200Znacznik szablonu {% url %} ...................................................................................................200U ycie get_absolute_url w przekierowaniach ......................................................................201

    11. Prosty formularz ........................................................................................................205Przeniesienie do formularza logiki odpowiedzialnej za sprawdzanie poprawno ci danych .... 205

    U ycie testu jednostkowego do analizy API formularzy ...................................................206Przej cie do Django ModelForm .............................................................................................208Testowanie i dostosowanie do w asnych potrzeb logiki weryfikacji formularza ..........209

    U ycie formularza w widokach ..................................................................................................... 210U ycie formularza w widoku za pomoc dania GET .....................................................211Du a operacja znajd i zast p ..................................................................................................213

    U ycie formularza w widoku obs uguj cym dania POST .................................................... 215Adaptacja testów jednostkowych dla widoku new_list ......................................................215U ycie formularza w widoku ..................................................................................................216U ycie formularza w celu wy wietlenia b dów w szablonie ...........................................216

    U ycie formularza w innym widoku ............................................................................................ 217Metoda pomocnicza dla wielu krótkich testów ....................................................................218

    U ycie metody save() formularza ................................................................................................. 220

    12. Bardziej skomplikowane formularze ........................................................................223Kolejny test funkcjonalny dotycz cy powielonych elementów ............................................... 223

    Ochrona przed duplikatami w warstwie modelu ................................................................224Ma a dygresja dotycz ca kolejno ci API Querystring i przedstawiania ci gu tekstowego ...226

    Poleć książkęKup książkę

    http://helion.pl/rf/tddwprhttp://helion.pl/rt/tddwpr

  • Spis tre ci 9

    Przepisanie testu starego modelu ...........................................................................................228Pewne b dy spójno ci ujawniaj si podczas zapisu .........................................................229

    Eksperymenty w warstwie widoku sprawdzaj ce, czy s powielone elementy .................. 230Bardziej skomplikowany formularz do obs ugi unikalno ci elementów ............................... 231U ycie istniej cego formularza w widoku listy .......................................................................... 232

    13. Zag biamy si ostro nie w JavaScript .................................................................... 237Rozpoczynamy od testów funkcjonalnych .................................................................................. 237Konfiguracja prostego silnika wykonywania testów JavaScript .............................................. 238U ycie jQuery i sta ych elementów ................................................................................... 240Utworzenie testu jednostkowego JavaScript dla danej funkcjonalno ci ............................ 243Testowanie JavaScript w cyklu TDD ............................................................................................ 245Zdarzenie onload i przestrzenie nazw ......................................................................................... 245Kilka rozwi za , które si nie sprawdzaj .................................................................................. 246

    14. Wdro enie nowego kodu ..........................................................................................247Wdro enie prowizoryczne .............................................................................................................. 247Wdro enie rzeczywiste .................................................................................................................... 247A je li wyst pi b d bazy danych? ................................................................................................ 248Podsumowanie — git tag i nowe wydanie .................................................................................. 248

    III Bardziej zaawansowane zagadnienia ............................................... 249

    15. U ycie JavaScript do uwierzytelniania u ytkownika,integracji wtyczek i przygotowania imitacji ............................................................ 251Mozilla Persona (BrowserID) ......................................................................................................... 252Kod eksperymentalny, czyli „Spiking” ........................................................................................ 252

    Utworzenie nowej ga zi dla Spike .........................................................................................253czenie kodu JavaScript i interfejsu u ytkownika .............................................................253

    Protokó Browser-ID ..................................................................................................................254Kod po stronie serwera — niestandardowe uwierzytelnienie ..........................................255

    Zamiana rozwi zania eksperymentalnego na zwyk e .............................................................. 260Cz sto stosowana technika Selenium — wyra ne oczekiwanie ........................................262Wycofanie kodu eksperymentalnego .....................................................................................264

    Testy jednostkowe JavaScript obejmuj ce komponenty zewn trzne— nasze pierwsze imitacje ........................................................................................................... 265

    Porz dkowanie — katalog plików statycznych dla ca ej witryny ....................................265Imitacja: kto, co i dlaczego? ......................................................................................................266Przestrzenie nazw ......................................................................................................................267Prosta imitacja dla testów jednostkowych dla naszej funkcji inicjuj cej .........................267Bardziej zaawansowane imitacje .............................................................................................272Sprawdzenie wywo ania argumentów ..................................................................................275Konfiguracja QUnit i testowanie da Ajax ........................................................................276Wi cej zagnie d onych wywo a zwrotnych! Testowanie kodu asynchronicznego ....280

    Poleć książkęKup książkę

    http://helion.pl/rf/tddwprhttp://helion.pl/rt/tddwpr

  • 10 Spis tre ci

    16. Uwierzytelnianie po stronie serwera i imitacje w Pythonie ...................................283Rzut oka na wersj eksperymentaln widoku logowania ........................................................ 283Imitacje w Pythonie .......................................................................................................................... 284

    Testowanie widoku za pomoc imitacji funkcji uwierzytelnienia ....................................284Sprawdzenie, czy widok faktycznie loguje u ytkownika ..................................................286

    Zmiana eksperymentalnej wersji uwierzytelniania na zwyk— imitacja dania internetowego .............................................................................................. 290

    Polecenie if oznacza wi cej testów ..........................................................................................291Poprawki na poziomie klasy ....................................................................................................292Strze si imitacji w porównaniach warto ci boolowskich ................................................295Utworzenie u ytkownika, je li to konieczne ........................................................................296Metoda get_user() ......................................................................................................................296

    Minimalny niestandardowy model u ytkownika ...................................................................... 298Ma e rozczarowanie ...................................................................................................................300Testy jako dokumentacja ..........................................................................................................301U ytkownicy s uwierzytelnieni .............................................................................................301

    Chwila prawdy — czy testy funkcjonalne zostan zaliczone? ................................................ 302Zako czenie testu funkcjonalnego, przetestowanie wylogowania ......................................... 303

    17. Konfiguracja testu, rejestracja i debugowanie po stronie serwera ........................ 307Pomini cie procesu logowania przez wst pne utworzenie sesji ............................................. 307

    Sprawdzamy rozwi zanie ........................................................................................................309Dowód znajdziesz w praktyce — u ycie wersji prowizorycznej do wychwycenia b dów ... 310

    Konfiguracja rejestracji danych ................................................................................................311Usuni cie b du systemu Persona ...........................................................................................312

    Zarz dzanie testow baz danych w serwerze prowizorycznym ........................................... 314Polecenie Django s u ce do tworzenia sesji ........................................................................314Test funkcjonalny uruchamiaj cy w serwerze narz dzie zarz dzania ............................315Dodatkowy krok za pomoc modu u subprocess ................................................................317

    Zachowanie kodu odpowiedzialnego za rejestracj danych .................................................... 320U ycie konfiguracji hierarchicznej rejestracji danych .........................................................320

    Podsumowanie .................................................................................................................................. 322

    18. Ko czymy „Moje listy” — podej cie Outside-In ......................................................325Alternatywa, czyli podej cie Inside-Out ...................................................................................... 325Dlaczego preferowane jest podej cie Outside-In? ...................................................................... 326Test funkcjonalny dla strony Moje listy ....................................................................................... 326Warstwa zewn trzna — prezentacja i szablony ......................................................................... 327Przej cie o jedn warstw w dó do funkcji widoku (kontroler) ............................................. 328Kolejne zaliczenie — podej cie Outside-In .................................................................................. 329

    Szybka restrukturyzacja hierarchii dziedziczenia szablonu ..............................................329Projektowanie API za pomoc szablonu ...............................................................................330Przej cie w dó do kolejnej warstwy — co widok przekazuje szablonowi? ...................331

    Kolejne „wymaganie” z warstwy widoku — nowe listy powinny „zapami tywa ”swego w a ciciela ........................................................................................................................... 332

    Czy przej do kolejnej warstwy, gdy test ko czy si niepowodzeniem? ......................333

    Poleć książkęKup książkę

    http://helion.pl/rf/tddwprhttp://helion.pl/rt/tddwpr

  • Spis tre ci 11

    Przej cie w dó do warstwy modelu ............................................................................................. 333Ostatni krok — uzyskanie z poziomu szablonu dost pu do w a ciciela

    za pomoc API .name .............................................................................................................335

    19. Izolacja i „s uchanie” testów .................................................................................... 337Powrót do miejsca, w którym podj li my decyzj — warstwa widoku

    zale y od nieutworzonego jeszcze kodu modelu .................................................................... 337Pierwsza próba u ycia imitacji w celu zapewnienia izolacji .................................................... 338

    U ycie side_effect do sprawdzenia sekwencji zdarze ......................................................339Pos uchaj testu — brzydki test oznacza konieczno refaktoryzacji ....................................... 341Ponowne utworzenie testów dla widoku, tym razem w pe ni odizolowanych ................... 342

    Pozostawienie starych zintegrowanych testów jako punktu odniesienia .......................342Nowy zestaw w pe ni odizolowanych testów ......................................................................342My limy w kategoriach wspó pracy .......................................................................................343

    Przej cie w dó do warstwy formularzy ....................................................................................... 347Nadal s uchaj testów — usuni cie kodu ORM z aplikacji ..................................................348

    Wreszcie przechodzimy w dó do warstwy modelu ................................................................. 350Powrót do widoków ..................................................................................................................352

    Moment prawdy (i ryzyko zwi zane z imitacjami) ................................................................... 353Potraktowanie interakcji mi dzy warstwami jak kontraktów ................................................. 354

    Identyfikacja niejawnych kontraktów ....................................................................................355Usuni cie przeoczonego problemu .........................................................................................356

    Jeszcze jeden test ............................................................................................................................... 357Porz dkowanie, czyli co zachowa z pakietu testów zintegrowanych? ................................ 358

    Usuni cie powielonego kodu w warstwie formularzy .......................................................358Usuni cie starej implementacji widoku .................................................................................359Usuni cie zb dnego kodu w warstwie formularzy .............................................................359

    Podsumowanie — testy odizolowane kontra zintegrowane .................................................... 360Niech poziom skomplikowania b dzie Twoim przewodnikiem ......................................361Czy powinienem tworzy oba rodzaje testów? ....................................................................361Do przodu! ...................................................................................................................................362

    20. Ci g a integracja ........................................................................................................363Instalacja serwera Jenkins ............................................................................................................... 363

    Konfiguracja zabezpiecze w Jenkins ....................................................................................365Dodanie wymaganych wtyczek ..............................................................................................365

    Konfiguracja projektu ...................................................................................................................... 367Pierwsza kompilacja ......................................................................................................................... 368Konfiguracja ekranu wirtualnego, aby testy funkcjonalne mo na by o wykonywa

    bez monitora ................................................................................................................................... 370Wykonanie zrzutów ekranu ........................................................................................................... 371Najcz stszy problem w Selenium — stan wy cigu .................................................................... 374Wykonanie testów QUnit w Jenkins za pomoc PhantomJS ................................................... 376

    Instalacja node ............................................................................................................................377Dodanie kolejnych kroków kompilacji w Jenkins ................................................................378

    Wi cej zada do wykonania za pomoc serwera ci g ej integracji ......................................... 380

    Poleć książkęKup książkę

    http://helion.pl/rf/tddwprhttp://helion.pl/rt/tddwpr

  • 12 Spis tre ci

    21. Token serwisów spo eczno ciowych, wzorzec strony i wiczenie dla czytelnika ... 381Test funkcjonalny z wieloma u ytkownikami i funkcja addCleanup() ................................. 381Implementacja w Selenium wzorca interakcja-oczekiwanie .................................................... 383Wzorzec strony ................................................................................................................................. 384Rozszerzenie testu funkcjonalnego na drugiego u ytkownika i stron „Moje listy” .......... 386

    wiczenie dla czytelnika ................................................................................................................. 388

    22. Szybkie testy, wolne testy i gor ca lawa ................................................................. 391Teza — testy jednostkowe s niezwykle szybkie, maj tak e inne zalety ............................. 392

    Szybsze testy oznaczaj szybsze tworzenie kodu ................................................................392Uczucie b ogostanu ....................................................................................................................393Wolne testy nie s wykonywane zbyt cz sto, co przek ada si na gorszej jako ci kod ....393Teraz jest dobrze, ale wraz z up ywem czasu testy zintegrowane

    s wykonywane coraz wolniej ..............................................................................................393Nie zabieraj mi tego ...................................................................................................................393Testy jednostkowe pozwalaj przygotowa dobry projekt ................................................394

    Problemy zwi zane z czystymi testami jednostkowymi ........................................................... 394Testy odizolowane mog by trudniejsze w odczycie i zapisie ........................................394Testy odizolowane nie testuj automatycznie integracji ....................................................394Testy jednostkowe rzadko przechwytuj nieoczekiwane b dy .......................................394Testy oparte na imitacji staj si ci le powi zane z implementacj ................................394Jednak wszystkie wymienione problemy mo na pokona ................................................395

    Synteza — jakie mamy oczekiwania wobec testów? ................................................................. 395Poprawno .................................................................................................................................395Czytelny, atwy w obs udze kod .............................................................................................395Produktywna praca ....................................................................................................................395Oce testy pod k tem korzy ci, jakich oczekujesz dzi ki ich u yciu ...............................396

    Rozwi zania architektoniczne ........................................................................................................ 396Porty i adaptery, czysta architektura i architektura heksagonalna ..................................397Architektura Functional Core, Imperative Shell ...................................................................398

    Podsumowanie .................................................................................................................................. 398

    Kieruj si Testing Goat! ............................................................................................. 401

    Dodatki ................................................................................................. 403A PythonAnywhere .......................................................................................................405B Widoki oparte na klasach Django .............................................................................409C Przygotowanie serwera za pomoc Ansible ............................................................ 419

    D Testowanie migracji bazy danych ............................................................................423E Co dalej? .....................................................................................................................429F ci ga .........................................................................................................................433

    G Bibliografia ................................................................................................................437

    Skorowidz ..................................................................................................................439

    Poleć książkęKup książkę

    http://helion.pl/rf/tddwprhttp://helion.pl/rt/tddwpr

  • 73

    ROZDZIA 5.

    Zapis danych wej ciowych u ytkownika

    Nasza aplikacja pobiera wpisany przez u ytkownika element listy rzeczy do zrobienia i prze-kazuje go do serwera. Dlatego te mo emy gdzie zapisa wspomniany element i wczyta gopó niej, gdy zajdzie potrzeba.

    Kiedy zaczyna em pisa ten rozdzia , od pocz tku zastanawia em si nad odpowiednim roz-wi zaniem dla naszej aplikacji: wiele modeli dla list i elementów listy, wiele ró nych adre-sów URL przeznaczonych do dodawania nowych list i elementów, trzy nowe funkcje widokui kilka nowych testów jednostkowych dla wymienionych wcze niej komponentów. W pewnymmomencie zatrzyma em si . Wprawdzie uzna em si za wystarczaj co sprytnego, aby jedno-cze nie ogarn wszystkie wymienione problemy, ale istota technik TDD polega na umo li-wieniu programi cie wykonywania zada pojedynczo, gdy zachodzi taka potrzeba. Zdecy-dowa em si wi c na celowy skrót i wykonywanie w danym momencie tylko tych zada ,które s niezb dne do posuni cia nieco naprzód testów funkcjonalnych.

    Pokazuj tutaj, jak techniki TDD mog obs ugiwa iteracyjny styl programowania. Nie jest tonajszybsza droga, ale ostatecznie i tak by si spotka z tego rodzaju podej ciem. Pozytyw-nym efektem ubocznym przyj tego podej cia b dzie mo liwo wprowadzenia nowych kon-cepcji, takich jak modele, praca z daniami POST, znaczniki szablonów Django itd. Wymie-nione koncepcje b d móg przedstawia pojedynczo, zamiast jednocze nie zarzuci Ci nimiwszystkimi.

    To oczywi cie nie oznacza, e nie powiniene próbowa my le z wyprzedzeniem i by sprytnym.W nast pnym rozdziale w znacznie wi kszym stopniu wykorzystamy projektowanie orazprzygotowywanie interfejsu i przekonasz si , jak to si wpisuje w stosowanie technik TDD.W tym rozdziale mo esz si nad tym jeszcze nie zastanawia i po prostu robi to, czego wy-magaj od nas testy.

    Od formularza sieciowego do wykonania dania POSTNa ko cu poprzedniego rozdzia u komunikaty generowane przez testy wskazuj na brakmo liwo ci zapisu danych wej ciowych u ytkownika. Teraz wykorzystamy standardowe -danie POST w HTML. Wprawdzie to nieco nudne, ale jednocze nie elegancie i atwe do osi -gni cia rozwi zanie, a ponadto pozwoli nam na u ycie kodu HTML5 i JavaScript w dalszejcz ci ksi ki.

    Poleć książkęKup książkę

    http://helion.pl/rf/tddwprhttp://helion.pl/rt/tddwpr

  • 74 Rozdzia 5. Zapis danych wej ciowych u ytkownika

    Aby przegl darka internetowa wygenerowa a danie POST, element musi posiadaatrybut name= i zosta opakowany znacznikiem wraz z atrybutem method="POST".W takim przypadku przegl darka internetowa automatycznie zajmie si wygenerowa-niem dania POST. Spróbujmy dostosowa szablon lists/templates/home.html do wymienionychwymaga .

    Plik lists/templates/home.html:

    Twoja lista rzeczy do zrobienia

    Teraz po wykonaniu naszych testów funkcjonalnych otrzymamy nieco zawi y i nieoczeki-wany b d:

    $ python3 functional_tests.py[...]Traceback (most recent call last): File "functional_tests.py", line 39, intest_can_start_a_list_and_retrieve_it_later table = self.browser.find_element_by_id('id_list_table')[...]selenium.common.exceptions.NoSuchElementException: Message: 'Unable to locateelement: {"method":"id","selector":"id_list_table"}' ; Stacktrace [...]

    Kiedy test funkcjonalny ko czy si nieoczekiwanym niepowodzeniem, wówczas mo emypodj wiele ró nych dzia a , aby odszuka ród o b du:

    Dodanie polece print w celu wy wietlania na przyk ad tekstu bie cej strony.

    Usprawnienie komunikatu b du, aby wy wietla wi cej informacji o bie cym stanie.

    R czne odwiedzenie problematycznej witryny.

    U ycie wywo ania time.sleep() w celu zrobienia przerwy podczas wykonywania testu.

    W trakcie lektury ksi ki spotkasz si z wszystkimi wymienionymi powy ej dzia aniami.Wywo anie time.sleep() to opcja, z której osobi cie korzystam bardzo cz sto. Wypróbujmy wi cto rozwi zanie w omawianym przyk adzie. Przerw dodamy przed wyst pieniem b du.

    # Po naci ni ciu klawisza Enter strona zosta a uaktualniona i wy wietla# "1: Kupi pawie pióra" jako element listy rzeczy do zrobienia.inputbox.send_keys(Keys.ENTER)

    import timetime.sleep(10)table = self.browser.find_element_by_id('id_list_table')

    W zale no ci od szybko ci dzia ania narz dzia Selenium w Twoim komputerze ród o pro-blemu mog e dostrzec ju wcze niej. Jednak po ponownym wykonaniu testów funkcjonal-nych masz wystarczaj co du o czasu na zobaczenie, co tak naprawd si dzieje. Na ekraniepowiniene zobaczy stron podobn do pokazanej na rysunku 5.1 i wy wietlaj c wiele wy-generowanych przez Django informacji o b dzie.

    Poleć książkęKup książkę

    http://helion.pl/rf/tddwprhttp://helion.pl/rt/tddwpr

  • Od formularza sieciowego do wykonania dania POST 75

    Rysunek 5.1. Framework Django wy wietla informacje o b dzie CSRF

    Zabezpieczenia — zaskakuj co zabawne!Je eli nigdy wcze niej nie s ysza e o atakach typu CSRF (ang. cross-site request forgery), towarto teraz nadrobi t zaleg o . Podobnie jak w przypadku wszystkich luk w zabezpiecze-niach lektura o genialnych rozwi zaniach pozwalaj cych na wykorzystanie systemu w nie-oczekiwany sposób mo e dostarczy wiele rado ci…

    Kiedy wróci em na uniwersytet, aby uzyska dyplom z informatyki, zapisa em si na zaj ciaz zakresu bezpiecze stwa. Pomy la em wtedy: có , to prawdopodobnie b d bardzo nudne zaj cia,ale lepiej, je li b d w nich uczestniczy . Okaza o si , e to by y najbardziej fascynuj ce zaj cia,jakie mia em na ca ych studiach. W takcie tych zaj du o czasu po wi cili my na hackingoraz rozwa ania, jak systemy mo na wykorzystywa na zupe nie nieoczekiwane sposoby.

    Jako lektur mog poleci pozycj , z której korzysta em podczas moich zaj — In ynieria za-bezpiecze 1 napisana przez Rossa Andersona. W wymienionej ksi ce nie znajdziesz zbytwiele o samej kryptografii, ale zosta a wype niona omówieniem innych interesuj cych tema-tów, takich jak wybór zabezpiecze , podrabianie informacji generowanych przez bank, eko-nomia kartrid ów w drukarkach atramentowych, a tak e oszukiwanie my liwców RPA zapomoc ataków metod powtórzenia. To jest do obszerna pozycja, ale zapewniam Ci , enie b dziesz móg si od niej oderwa .

    1 http://helion.pl/ksiazki/inzynieria-zabezpieczen-ross-anderson,a_000w.htm

    Poleć książkęKup książkę

    http://helion.pl/rf/tddwprhttp://helion.pl/rt/tddwpr

  • 76 Rozdzia 5. Zapis danych wej ciowych u ytkownika

    Oferowane przez framework Django zabezpieczenia przed atakami typu CSRF obejmujmi dzy innymi umieszczenie w ka dym wygenerowanym formularzu niewielkiego tokenu. Dzi kitemu danie POST mo na zidentyfikowa jako pochodz ce z pierwotnej witryny. Jak dot d oma-wiany szablon sk ada si wy cznie z kodu HTML. Kolejnym krokiem jest wi c wykorzystaniepo raz pierwszy oferowanej przez Django magii szablonów. W celu dodania tokenu CSRF u yjemytak zwanego znacznika szablonu sk adaj cego si z nawiasu klamrowego i znaku procentu. Wymie-niony znacznik jest znany jako najbardziej irytuj ca na wiecie kombinacja dwóch klawiszy.

    Plik lists/templates/home.html:

    {% csrf_token %}

    Podczas generowania strony Django zast pi dodany znacznik elementem zawieraj cym token CSRF. Ponowne wykonanie testu zako czy si teraz oczekiwanym nie-powodzeniem:

    AssertionError: False is not true : Nowy element nie znajduje si w tabeli.

    Poniewa w kodzie nadal znajduje si wywo anie time.sleep(), wykonywanie testu zostanieprzerwane na pewien czas i b dziesz móg zobaczy , e nowy element listy znika po wys aniuformularza, a strona zostaje od wie ona i ponownie wy wietla pusty formularz. Po prostunie skonfigurowali my jeszcze serwera do obs ugi da POST. Dlatego te s one ignorowane,a przegl darka internetowa wy wietla zwyk stron g ówn .

    Teraz mo emy ju usun wywo anie time.sleep().

    Plik functional_tests.py:

    # "1: Kupi pawie pióra" jako element listy rzeczy do zrobienia.inputbox.send_keys(Keys.ENTER)table = self.browser.find_element_by_id('id_list_table')

    Przetwarzanie dania POST w serwerzePoniewa w formularzu sieciowym nie u yli my atrybutu action=, po jego wys aniu nast -puje domy lnie przej cie do adresu URL, w którym nast pi o wygenerowanie formularza (naprzyk ad /). W omawianym przypadku wy wietlanie strony jest obs ugiwane przez funkcjhome_page(). Przystosujmy wi c widok do obs ugi dania POST.

    To oznacza konieczno utworzenia nowego testu jednostkowego dla widoku home_page.Otwórz plik lists/tests.py i dodaj now metod do HomePageTest. Osobi cie skopiowa em po-przedni metod , a nast pnie przystosowa em j do obs ugi dania POST i upewni em si , ezwrócony kod HTML zawiera tekst nowego elementu listy rzeczy do zrobienia.

    Plik lists/tests.py (ch05l005):

    def test_home_page_returns_correct_html(self): [...]

    def test_home_page_can_save_a_POST_request(self): request = HttpRequest() request.method = 'POST' request.POST['item_text'] = 'Nowy element listy'

    response = home_page(request)

    self.assertIn('Nowy element listy', response.content.decode())

    Poleć książkęKup książkę

    http://helion.pl/rf/tddwprhttp://helion.pl/rt/tddwpr

  • Przekazanie zmiennych Pythona do wygenerowania w szablonie 77

    Czy zastanowi y Ci puste wiersze w kodzie nowej metody? Na pocz tku zgrupo-wa em trzy wiersze zawieraj ce konfiguracj testu. Jeden wiersz w rodku to fak-tyczne wywo anie testowanej funkcji, natomiast na ko cu mamy asercj . Wprawdziestosowanie takiego rozwi zania nie jest obligatoryjne, ale pomaga w dostrze eniustruktury testu. Konfiguracja, sprawdzenie i asercja to typowa struktura testu jed-nostkowego.

    Jak mo esz zobaczy , w kodzie u yli my dwóch atrybutów specjalnych obiektu HttpRequest:.method i .POST (ich nazwy powinny wyra nie wskazywa przeznaczenie atrybutów, ale i takwarto zajrze do po wi conej im dokumentacji2 w witrynie Django). Nast pnie sprawdzamy, czytekst przekazany w daniu POST znajduje si w wygenerowanym kodzie HTML. Wynikiemjest oczekiwane niepowodzenie testu:

    $ python3 manage.py test[...]AssertionError: 'Nowy element listy' not found in ' [...]

    Test mo e zosta zaliczony przez dodanie polecenia if i dostarczenie zupe nie innego koduprzeznaczonego do obs ugi da POST. W typowym stylu TDD rozpoczynamy od celowegozdefiniowania zupe nie nieprawid owej warto ci zwrotnej.

    Plik lists/views.py:

    from django.http import HttpResponsefrom django.shortcuts import render

    def home_page(request): if request.method == 'POST': return HttpResponse(request.POST['item_text']) return render(request, 'home.html')

    W ten sposób test jednostkowy zostaje zaliczony, ale tak naprawd to nie jest rozwi zanie,które nas interesuje. Naszym celem jest umieszczenie w tabeli wy wietlanej na stronie g ównejdanych przekazanych przez danie POST.

    Przekazanie zmiennych Pythonado wygenerowania w szablonieMieli my ju przedsmak, a teraz zaczniemy w pe ni wykorzystywa pot ne mo liwo ci sk adniszablonów Django, co pozwoli na przekazywanie zmiennych z kodu widoku Pythona do sza-blonów HTML.

    Na pocz tek musisz zobaczy , jak sk adnia szablonu pozwala na umieszczenie w nim obiektuPythona. Notacja w postaci {{ ... }} wy wietla obiekt jako ci g tekstowy.

    Plik lists/templates/home.html:

    Twoja lista rzeczy do zrobienia {% csrf_token %}

    2 https://docs.djangoproject.com/en/1.7/ref/request-response/

    Poleć książkęKup książkę

    http://helion.pl/rf/tddwprhttp://helion.pl/rt/tddwpr

  • 78 Rozdzia 5. Zapis danych wej ciowych u ytkownika

    {{ new_item_text }}

    W jaki sposób mo na sprawdzi , czy widok otrzymuje prawid ow warto dla new_item_text?Jak w ogóle mo na przekaza zmienn do szablonu? Mo emy si o tym przekona , faktyczniewykonuj c test jednostkowy. Funkcji render_to_string() u yli my w poprzednim te cie jed-nostkowym w celu r cznego wygenerowania szablonu i porównania go z kodem HTML wy-generowanym przez widok. Teraz dodamy zmienn , która ma zosta przekazana.

    Plik lists/tests.py:

    self.assertIn('Nowy element listy', response.content.decode())expected_html = render_to_string( 'home.html', {'new_item_text': 'Nowy element listy'})self.assertEqual(response.content.decode(), expected_html)

    Jak mo esz zobaczy , funkcja render_to_string() pobiera jako drugi argument mapowanienazw zmiennych na warto ci. Szablon otrzymuje zmienn o nazwie new_item_text, którejwarto ci powinien by tekst pobrany z dania POST.

    Po wykonaniu testu jednostkowego funkcja render_to_string() zast pi wewn trz elementu notacj {{ new_item_text }} warto ci Nowy element listy. Rzeczywisty widok nie wyko-nuje takiej operacji, a wi c powinni my spodziewa si niepowodzenia testu:

    self.assertEqual(response.content.decode(), expected_html)AssertionError: 'Nowy element listy' != '\n \n [...]

    Doskonale. Celowo ustalona wcze niej nieprawdziwa warto zwrotna nie b dzie d u ejoszukiwa a testów. Mo emy wi c zmodyfikowa kod widoku i nakaza mu przekazanie pa-rametru POST do szablonu.

    Plik lists/views.py (ch05l009):

    def home_page(request): return render(request, 'home.html', { 'new_item_text': request.POST['item_text'], })

    Teraz ponownie wykonujemy test jednostkowy:ERROR: test_home_page_returns_correct_html (lists.tests.HomePageTest)[...] 'new_item_text': request.POST['item_text'],KeyError: 'item_text'

    Wynikiem testu jest oczekiwane niepowodzenie.

    Je eli przypomnisz sobie regu y odczytu stosu wywo a , to zauwa ysz, e niepowodzeniemzako czy si inny test. Uda o nam si osi gn zaliczenie testu, nad którym pracowali my.Natomiast wybrane testy jednostkowe spowodowa y powstanie nieoczekiwanych konse-kwencji w postaci regresji — uszkodzili my funkcjonalno kodu przeznaczonego do obs ugisytuacji, gdy nie wyst puje danie POST.

    Teraz ju widzisz, jak wa ne jest przygotowywanie testów. Wprawdzie w omawianymprzypadku mo na si by o spodziewa uszkodzenia funkcjonalno ci, ale wyobra sobie sytu-acj , gdy masz z y dzie lub po prostu nie zwróci e wystarczaj cej uwagi. Wówczas testy

    Poleć książkęKup książkę

    http://helion.pl/rf/tddwprhttp://helion.pl/rt/tddwpr

  • Przekazanie zmiennych Pythona do wygenerowania w szablonie 79

    uchroni przed uszkodzeniem funkcjonalno ci aplikacji, a poniewa stosujemy techniki TDD,to dowiemy si o tym natychmiast. Nie trzeba czeka na reakcj dzia u odpowiedzialnego zakontrol jako ci lub te przechodzi do przegl darki internetowej i r cznie sprawdza witryn .Problem mo na usun od razu, a rozwi zanie przedstawiono poni ej.

    Plik lists/views.py:

    def home_page(request): return render(request, 'home.html', { 'new_item_text': request.POST.get('item_text', ''), })

    Je eli nie jeste pewien, jak dzia a powy sze rozwi zanie, spójrz na dict.get().

    Test jednostkowy powinien zosta teraz zaliczony. Zobaczmy, jak przedstawia si wynik testufunkcjonalnego:

    AssertionError: False is not true : Nowy element nie znajduje si w tabeli.

    Có , komunikat b du nie okazuje si szczególnie u yteczny. Wykorzystamy wi c kolejn techni-k usuwania b dów z testów funkcjonalnych, czyli poprawimy komunikat b du. To jest praw-dopodobnie najbardziej konstruktywna technika, poniewa poprawione komunikaty b dówju pozostan w aplikacji i b d pomocne podczas usuwania b dów w przysz o ci.

    Plik functional_tests.py:

    self.assertTrue( any(row.text == '1: Kupi pawie pióra' for row in rows), "Nowy element nie znajduje si w tabeli -- jego tekst to:\n%s" % ( table.text, ))

    W ten sposób otrzymujemy znacznie u yteczniejszy komunikat b du:AssertionError: False is not true : Nowy element nie znajduje si w tabeli --jego tekst to:Kupi pawie pióra

    Czy wiesz, e mo na przygotowa jeszcze lepsze rozwi zanie ni obecne? Zastosowanie wska-zanej asercji nie jest a tak sprytnym podej ciem. Jak zapewne pami tasz, by em zadowolonyz wykorzystania funkcji any(). Jednak jeden z pierwszych czytelników ksi ki (dzi kuj , Jasonie!)zasugerowa mi u ycie znacznie prostszej implementacji. Wszystkie sze wierszy assertTrue()mo na zast pi pojedynczym wierszem assertIn().

    Plik functional_tests.py:

    self.assertIn('1: Kupi pawie pióra', [row.text for row in rows])

    Znacznie lepiej. Zawsze warto zastanowi si , czy mo na zastosowa inne, sprytniejsze roz-wi zanie, poniewa prawdopodobnie u ywasz niepotrzebnie zbyt skomplikowanego. Bonusemb dzie otrzymanie odpowiedniego komunikatu b du:

    self.assertIn('1: Kupi pawie pióra', [row.text for row in rows])AssertionError: '1: Kupi pawie pióra' not found in ['Kupi pawie pióra']

    Mam nadziej , e dobrze si tutaj zrozumiemy. Test funkcjonalny oczekuje otrzymania elementunumerowanej listy wraz ze znakami 1: na pocz tku pierwszego elementu. Najszybszym sposo-bem zaliczenia testu b dzie ma e „oszustwo” w postaci modyfikacji wprowadzonej w szablonie.

    Plik lists/templates/home.html:

    1: {{ new_item_text }}

    Poleć książkęKup książkę

    http://helion.pl/rf/tddwprhttp://helion.pl/rt/tddwpr

  • 80 Rozdzia 5. Zapis danych wej ciowych u ytkownika

    Czerwony/zielony/refaktoryzacja i triangulacjaCykl test jednostkowy i tworzenie kodu jest czasami okre lany mianem czerwony, zielony,refaktoryzacja:

    Zaczynamy od utworzenia testu jednostkowego, którego wykonanie ko czy si niepo-wodzeniem (czerwony).

    Dodajemy minimaln mo liw ilo kodu w celu zaliczenia testu (zielony), nawet je lioznacza to konieczno uciekni cia si do oszustwa.

    Przeprowadzamy refaktoryzacj , aby otrzyma kod lepszej jako ci i bardziej przejrzysty.

    Jakie dzia ania podejmujemy na etapie refaktoryzacji? Co usprawiedliwia przej cie od im-plementacji, w której „oszukujemy”, do implementacji, z której jeste my zadowoleni?

    Jedn z metodologii jest eliminacja powielania. Je eli test u ywa magicznej sta ej (na przyk adznaków 1: na pocz tku elementu listy, jak w omawianej sytuacji), wówczas kod aplikacjirównie korzysta z tej sta ej. Mamy wi c do czynienia z powielaniem kodu, co jest wystar-czaj cym powodem do przeprowadzenia refaktoryzacji. Usuni cie magicznej sta ej z koduaplikacji zwykle oznacza konieczno zaprzestania oszukiwania.

    Przekona em si , e powy sze rozwi zanie oznacza powstanie nieco niejednoznacznego kodu.Dlatego te najcz ciej decyduj si na drug technik , o nazwie triangulacja. Je eli test mo -na zaliczy , tworz c „oszukuj cy” kod, z którego nie jeste zadowolony (na przyk ad zwrotmagicznej sta ej), wówczas utwórz kolejny test wymuszaj cy opracowanie lepszego kodu. Takiepodej cie zastosujemy podczas rozbudowy testu funkcjonalnego o sprawdzenie, czy znaki 2:znajduj si na pocz tku drugiego elementu listy rzeczy do zrobienia.

    Teraz docieramy do wywo ania self.fail('Zako czenie testu!'). Je eli rozbudujemy testfunkcjonalny (metod kopiuj i wklej, mój przyjacielu) o sprawdzenie, czy drugi element listy zo-sta dodany do tabeli, to przekonamy si , e nasze rozwi zanie tak naprawd jest niezbyt dobre.

    Plik functional_tests.py:

    # Na stronie nadal znajduje si pole tekstowe zach caj ce do podania kolejnego zadania.# Edyta wpisa a "U y pawich piór do zrobienia przyn ty" (Edyta jest niezwykle skrupulatna).inputbox = self.browser.find_element_by_id('id_new_item')inputbox.send_keys('U y pawich piór do zrobienia przyn ty')inputbox.send_keys(Keys.ENTER)

    # Strona zosta a ponownie uaktualniona i teraz wy wietla dwa elementy na li cie rzeczy do zrobienia.table = self.browser.find_element_by_id('id_list_table')rows = table.find_elements_by_tag_name('tr')self.assertIn('1: Kupi pawie pióra', [row.text for row in rows])self.assertIn( '2: U y pawich piór do zrobienia przyn ty' , [row.text for row in rows])# Edyta by a ciekawa, czy witryna zapami ta jej list . Zwróci a uwag na# wygenerowany dla niej unikatowy adres URL, obok którego znajduje si# pewien tekst z wyja nieniem.self.fail('Zako czenie testu!')

    # Przechodzi pod podany adres URL i widzi wy wietlon swoj list rzeczy do zrobienia.

    Zgodnie z oczekiwaniami nasz test funkcjonalny powoduje wygenerowanie b du:AssertionError: '1: Kupi pawie pióra' not found in ['1: Use peacockfeathers to make a fly']

    Poleć książkęKup książkę

    http://helion.pl/rf/tddwprhttp://helion.pl/rt/tddwpr

  • Do trzech razy sztuka, a pó niej refaktoryzacja 81

    Do trzech razy sztuka, a pó niej refaktoryzacjaZanim przejdziemy dalej, mamy nieprzyjemny zapach kodu3 w przedstawionym te cie funk-cjonalnym. Istniej trzy prawie identyczne bloki kodu odpowiedzialne za sprawdzanie nowychelementów na li cie. Warto przypomnie sobie regu DRY (ang. don’t repeat yourself, nie po-wtarzaj si ), któr mo emy tutaj wykorzysta przez zastosowanie mantry do trzech razy sztuka,a pó niej refaktoryzacja. Raz mo na skopiowa kod i wklei go w innym miejscu; usuni cie tegorodzaju powielenia mo e okaza si przedwczesne. Jednak maj c ju trzy wyst pienia danegokodu, najwy sza pora na pozbycie si powielonego kodu.

    Prac rozpoczynamy od przekazania do repozytorium zmodyfikowanych dot d plików.Decydujemy si na to, wiedz c, e tworzona witryna zawiera powa ny b d (mo liwo ob-s ugi tylko jednego elementu listy), ale i tak poczynili my post py wzgl dem ostatniej wersjiznajduj cej si w repozytorium. By mo e utworzymy ca y kod od pocz tku, a mo e nie, aleregu jest, aby przed przeprowadzeniem jakiejkolwiek refaktoryzacji zawsze przekaza koddo repozytorium:

    $ git diff# Polecenie powinno wy wietli zmiany wprowadzone w plikach# functional_tests.py, home.html, tests.py i views.py.$ git commit -a

    Powracamy teraz do refaktoryzacji testu funkcjonalnego. Wprawdzie mo na u y funkcji typuinline, ale to na pewno nieco zak óci przebieg testu. Dlatego te wykorzystamy metod po-mocnicz — pami taj, e tylko metody o nazwach rozpoczynaj cych si od test_ zostan wy-konane jako testy. Pozosta ych metod mo na wi c u y do w asnych celów.

    Plik functional_tests.py:

    def tearDown(self): self.browser.quit()

    def check_for_row_in_list_table(self, row_text): table = self.browser.find_element_by_id('id_list_table') rows = table.find_elements_by_tag_name('tr')

    self.assertIn(row_text, [row.text for row in rows])

    def test_can_start_a_list_and_retrieve_it_later(self): [...]

    Metody pomocnicze lubi umieszcza gdzie na pocz tku klasy, mi dzy metod tearDown()i pierwszym testem. Umie my j w naszym te cie funkcjonalnym.

    Plik functional_tests.py:

    # Po naci ni ciu klawisza Enter strona zosta a uaktualniona i wy wietla# "1: Kupi pawie pióra" jako element listy rzeczy do zrobienia.inputbox.send_keys(Keys.ENTER)self.check_for_row_in_list_table('1: Kupi pawie pióra')

    # Na stronie nadal znajduje si pole tekstowe zach caj ce do podania kolejnego zadania.# Edyta wpisa a "U y pawich piór do zrobienia przyn ty" (Edyta jest niezwykle skrupulatna).

    3 Je eli nie znasz koncepcji zapachu kodu, to powiniene wiedzie , e oznacza ona ten fragment kodu, który zmu-

    sza Ci do jego refaktoryzacji. Jeff Atwood napisa interesuj cy artyku (http://blog.codinghorror.com/code-smells/)na ten temat. Im wi ksze do wiadczenie b dziesz zdobywa w obszarze programowania, tym bardziej b dzieszmia wyczulony nos na zapach kodu…

    Poleć książkęKup książkę

    http://helion.pl/rf/tddwprhttp://helion.pl/rt/tddwpr

  • 82 Rozdzia 5. Zapis danych wej ciowych u ytkownika

    inputbox = self.browser.find_element_by_id('id_new_item')inputbox.send_keys('U y pawich piór do zrobienia przyn ty')inputbox.send_keys(Keys.ENTER)

    # Strona zosta a ponownie uaktualniona i teraz wy wietla dwa elementy na li cie rzeczy do zrobienia.self.check_for_row_in_list_table('1: Kupi pawie pióra')self.check_for_row_in_list_table('2: U y pawich piór do zrobienia przyn ty')

    # Edyta by a ciekawa, czy witryna zapami ta jej list . Zwróci a uwag na[...]

    Po ponownym wykonaniu testu funkcjonalnego okazuje si , e jego zachowanie nie uleg ozmianie…

    AssertionError: '1: Kupi pawie pióra' not found in ['1: U y pawichpiór do zrobienia przyn ty']

    Dobrze. Teraz mo emy przekaza pliki do repozytorium i potraktowa refaktoryzacj jakoma , niezale n zmian :

    $ git diff # Przejrzenie zmian wprowadzonych w pliku functional_tests.py.$ git commit -a

    Powracamy do pracy. Je eli kiedykolwiek zajdzie potrzeba obs ugi wi cej ni tylko jednegoelementu listy, wtedy b dziemy potrzebowa pewnego rodzaju trwa ego magazynu danych.Baza danych to niezawodne rozwi zanie, które mo emy zastosowa w tym obszarze.

    Django ORM i nasz pierwszy modelORM (ang. object-relational mapper, mapowanie obiektowo-relacyjne) to warstwa abstrakcjiprzeznaczona dla danych przechowywanych w tabelach, rekordach i kolumnach bazy danych.Pozwala na prac z bazami danych za pomoc doskonale znanych metafor zorientowanychobiektowo, które sprawdzaj si w kodzie aplikacji. Klasy s mapowane na tabele, atrybutyna kolumny, a poszczególne egzemplarze klasy przedstawiaj rekordy danych przechowy-wanych w bazie danych.

    Framework Django jest dostarczany wraz z doskona warstw ORM. Utworzenie testu jed-nostkowego opartego na tej warstwie b dzie najlepszym sposobem jej poznania, poniewaprzeanalizujemy kod, przygotowuj c go do dzia ania w interesuj cy nas sposób.

    Rozpoczynamy od utworzenia nowej klasy w pliku lists/tests.py.

    Plik lists/tests.py:

    from lists.models import Item[...]

    class ItemModelTest(TestCase):

    def test_saving_and_retrieving_items(self): first_item = Item() first_item.text = 'Absolutnie pierwszy element listy' first_item.save()

    second_item = Item() second_item.text = 'Drugi element' second_item.save()

    Poleć książkęKup książkę

    http://helion.pl/rf/tddwprhttp://helion.pl/rt/tddwpr

  • Django ORM i nasz pierwszy model 83

    saved_items = Item.objects.all() self.assertEqual(saved_items.count(), 2)

    first_saved_item = saved_items[0] second_saved_item = saved_items[1] self.assertEqual(first_saved_item.text, 'Absolutnie pierwszy element listy') self.assertEqual(second_saved_item.text, 'Drugi element')

    Jak mo esz zobaczy , utworzenie nowego rekordu w bazie danych to wzgl dnie proste za-danie. Sprowadza si do utworzenia obiektu, przypisania mu pewnych atrybutów, a nast p-nie wywo ania funkcji save(). Django oferuje API przeznaczone do wykonywania zapytaw bazie danych za pomoc atrybutu klasy .objects. W omawianym przyk adzie u ywamy naj-prostszego z mo liwych zapyta .all(), które pobiera wszystkie rekordy ze wskazanej tabeli.Wynikiem jest przypominaj cy list obiekt o nazwie QuerySet, z którego mo na wyodr bniaposzczególne obiekty, a ponadto wywo ywa inne funkcje, na przyk ad count(). Nast pniesprawdzamy, czy obiekty zosta y zapisane w bazie danych, aby upewni si o zachowaniuw niej w a ciwych informacji.

    Oferowana przez Django warstwa ORM zawiera jeszcze wiele innych intuicyjnych w u yciufunkcji. Warto przynajmniej przejrze samouczek Django4 po wi cony ORM, który stanowi do-skona e wprowadzenie do wspomnianych funkcji.

    Przedstawiony powy ej test jednostkowy utworzy em w bardzo rozwlek ym stylui potraktowa em go jako wprowadzenie do warstwy ORM w Django. Dla klasy mo-delu mo esz przygotowa znacznie krótszy test, o czym si przekonasz w rozdziale 11.

    Terminologia 2: test jednostkowykontra test integracji a baza danych

    Pury ci b d si upiera , e „rzeczywisty” test jednostkowy nigdy nie powinien opiera sina bazie danych, a przygotowany tutaj test powinien by nazwany testem integracji, ponie-wa nie sprawdza jedynie kodu, ale opiera si równie na systemie zewn trznym, jakim tutajjest baza danych.

    Na chwil obecn mo emy zrezygnowa z rozró niania wymienionych testów. Mamy dwatypy testów. Pierwszy to wysokiego poziomu testy funkcjonalne przeznaczone do testowa-nia aplikacji z perspektywy u ytkownika. Drugi to niskiego poziomu testy przeznaczone dotestowania aplikacji z perspektywy programisty.

    Do tego tematu oraz omówienia testów jednostkowych i integracji powrócimy jeszcze w roz-dziale 19. i dalej w ksi ce.

    Spróbujmy teraz wykona test jednostkowy. Mamy do czynienia z kolejnym cyklem test jed-nostkowy — tworzenie kodu:

    ImportError: cannot import name 'Item'

    Doskonale. Zacznijmy od mo liwo ci zaimportowania czegokolwiek z pliku lists/models.py.Czujemy, e mo emy pomin krok Item = None i od razu przej do utworzenia klasy.

    4 https://docs.djangoproject.com/en/1.7/intro/tutorial01/

    Poleć książkęKup książkę

    http://helion.pl/rf/tddwprhttp://helion.pl/rt/tddwpr

  • 84 Rozdzia 5. Zapis danych wej ciowych u ytkownika

    Plik lists/models.py:

    from django.db import models

    class Item(object): pass

    Wykonanie testu ko czy si w nast puj cy sposób: first_item.save()AttributeError: 'Item' object has no attribute 'save'

    Aby mo na by o udost pni klasie Item metod save() i tym samym zmieni j w prawdziwymodel Django, musi ona dziedziczy po klasie Model.

    Plik lists/models.py:

    from django.db import models

    class Item(models.Model): pass

    Pierwsza migracja bazy danychMo emy si teraz spodziewa b du wygenerowanego przez baz danych:

    first_item.save() File "/usr/local/lib/python3.4/dist-packages/django/db/models/base.py", line593, in save[...] return Database.Cursor.execute(self, query, params)django.db.utils.OperationalError: no such table: lists_item

    W Django zadaniem warstwy ORM jest modelowanie bazy danych. Istnieje jeszcze drugisystem, o nazwie migracje, odpowiedzialny za faktyczne tworzenie bazy danych. Jego zada-niem jest umo liwienie programi cie dodawania i usuwania tabel oraz kolumn na podstawiezmian wprowadzanych w pliku models.py.

    Migracje mo esz potraktowa jako system kontroli wersji dla bazy danych. Jak si wkrótceprzekonasz, okazuje si to szczególnie u yteczne, gdy zachodzi potrzeba uaktualnienia bazydanych wdro onej w dzia aj cym serwerze.

    Na obecnym etapie musisz jedynie wiedzie , jak przygotowa pierwsz migracj bazy danych.Do tego celu wykorzystamy polecenie makemigrations:

    $ python3 manage.py makemigrationsMigrations for 'lists': 0001_initial.py: - Create model Item$ ls lists/migrations0001_initial.py __init__.py __pycache__

    Je eli jeste ciekaw, zajrzyj do plików migracji. Przekonasz si , e stanowi one reprezentacjzmian wprowadzonych w pliku models.py.

    W mi dzyczasie powinni my posun nasze testy nieco do przodu.

    Poleć książkęKup książkę

    http://helion.pl/rf/tddwprhttp://helion.pl/rt/tddwpr

  • Django ORM i nasz pierwszy model 85

    Zdumiewaj co du y post p w te cieOsi gn li my zdumiewaj cy post p w te cie:

    $ python3 manage.py test lists[...] self.assertEqual(first_saved_item.text, 'Absolutnie pierwszy element listy')AttributeError: 'Item' object has no attribute 'text'

    W porównaniu z poprzednim niepowodzeniem uda o nam si przej o osiem wierszy kodudo przodu. Poczynili my kroki maj ce na celu zachowanie w bazie danych dwóch obiektówItem i upewnili my si o ich zapisaniu w bazie danych. Wydaje si jednak, e Django nie za-chowa o atrybutu text.

    Nawiasem mówi c, je eli dopiero rozpoczynasz programowanie w Pythonie, to w ogóle mo-esz by zaskoczony mo liwo ci przypisania warto ci atrybutowi text. W j zykach takich

    jak Java tego rodzaju operacja prawdopodobnie zako czy si wygenerowaniem b du kom-pilacji. Pod tym wzgl dem Python jest znacznie elastyczniejszy.

    Klasy dziedzicz ce po models.Model odpowiadaj za mapowanie obiektów na tabele w baziedanych. Domy lnie otrzymuj automatycznie wygenerowany atrybut id b d cy kolumnklucza podstawowego w bazie danych. Jednak wszystkie pozosta e kolumny musisz wyra niezdefiniowa . Poni ej przedstawiono przyk ad pokazuj cy konfiguracj kolumny tekstowej.

    Plik lists/models.py:

    class Item(models.Model): text = models.TextField()

    Django oferuje wiele innych typów kolumn, na przyk ad IntegerField, CharField, DateField itd.Zdecydowa em si na TextField zamiast CharField, poniewa drugi z wymienionych typówwymaga zastosowania obszernych ogranicze , co na obecnym etapie wydaje si niepotrzebne.Wi cej informacji dotycz cych ró nych typów kolumn znajdziesz w samouczku5 Django orazw dokumentacji6.

    Nowa kolumna oznacza now migracjWykonanie testów powoduje wygenerowanie kolejnego b du bazy danych:

    django.db.utils.OperationalError: table lists_item has no column named text

    B d jest skutkiem dodania nowej kolumny do bazy danych, co oznacza konieczno prze-prowadzenia kolejnej migracji. Dobrze, e testy mog nas o tym poinformowa !

    Spróbujmy wi c przeprowadzi migracj :$ python3 manage.py makemigrationsYou are trying to add a non-nullable field 'text' to item without a default;we can't do that (the database needs something to populate existing rows).Please select a fix: 1) Provide a one-off default now (will be set on all existing rows) 2) Quit, and let me add a default in models.pySelect an option:2

    5 https://docs.djangoproject.com/en/1.7/intro/tutorial01/#creating-models6 https://docs.djangoproject.com/en/1.7/ref/models/fields/

    Poleć książkęKup książkę

    http://helion.pl/rf/tddwprhttp://helion.pl/rt/tddwpr

  • 86 Rozdzia 5. Zapis danych wej ciowych u ytkownika

    Hm, nie mo emy doda kolumny bez warto ci domy lnej. Wybieramy wi c opcj drug i ustalamywarto domy ln w pliku models.py. Jak s dz , sk adnia nie wymaga dodatkowych wyja nie .

    Plik lists/models.py:

    class Item(models.Model): text = models.TextField(default='')

    Teraz migracja powinna zako czy si powodzeniem:$ python3 manage.py makemigrationsMigrations for 'lists': 0002_item_text.py: - Add field text to item

    Po dodaniu dwóch nowych wierszy kodu w pliku models.py oraz po przeprowadzeniu dwóchmigracji bazy atrybut text obiektów modelu zostaje wreszcie uznany za atrybut specjalny i za-pisany w bazie danych. Skutkiem jest zaliczenie testu…

    $ python3 manage.py test lists[...]Ran 4 tests in 0.010sOK

    Do repozytorium mo emy wi c przekaza nasz pierwszy model!$ git status # Polecenie wy wietla pliki tests.py, models.py oraz dwie niemonitorowane migracje.$ git diff # Polecenie pozwala na przejrzenie zmian wprowadzonych w plikach tests.py i models.py.$ git add lists$ git commit -m"Model dla obiektów Items oraz powi zane z nim migracje."

    Zapis w bazie danych informacji z dania POSTDostosujmy teraz test dania POST strony g ównej. Przyjmujemy za o enie, e celem jest zapisanieprzez widok nowego elementu w bazie danych zamiast po prostu umieszczenie go w odpowiedzi.Wystarczy doda trzy nowe wiersze do istniej cego testu test_home_page_can_save_a_POST_request().

    Plik lists/tests.py:

    def test_home_page_can_save_a_POST_request(self): request = HttpRequest() request.method = 'POST' request.POST['item_text'] = 'Nowy element listy'

    response = home_page(request)

    self.assertEqual(Item.objects.count(), 1) # new_item = Item.objects.first() # self.assertEqual(new_item.text, 'Nowy element listy') #

    self.assertIn('Nowy element listy', response.content.decode()) expected_html = render_to_string( 'home.html', {'new_item_text': 'Nowy element listy'} ) self.assertEqual(response.content.decode(), expected_html)

    Sprawdzamy, czy nowy obiekt Item zosta zapisany w bazie danych. Wywo anie objects.count() to skrócona forma wywo ania objects.all().count().

    Wywo anie objects.first() ma taki sam efekt jak objects.all()[0].

    Sprawdzamy, czy tekst elementu jest prawid owy.

    Poleć książkęKup książkę

    http://helion.pl/rf/tddwprhttp://helion.pl/rt/tddwpr

  • Zapis w bazie danych informacji z dania POST 87

    Ten test jest nieco rozwlek y. Wydaje si , e testowana jest du a ilo ró nych rzeczy. Mamywi c kolejny przyk ad zapachu kodu — d ugi test jednostkowy, który powinien zosta po-dzielony na dwa lub jest sygna em, e istnieje zbyt skomplikowany przedmiot testu. T kwe-sti dodajmy do naszej krótkiej, osobistej listy rzeczy do zrobienia, zapisanej na przyk ad nakartce papieru (patrz rysunek 5.2).

    Rysunek 5.2. Nasza osobista lista rzeczy do zrobienia

    Zapisanie wymienionej kwestii na kartce papieru gwarantuje, e o niej nie zapomnimy. Terazmo emy spokojnie powróci do przerwanej pracy. Ponownie wykonujemy testy i otrzymu-jemy wynik w postaci oczekiwanego niepowodzenia:

    self.assertEqual(Item.objects.count(), 1)AssertionError: 0 != 1

    Przyst pujemy do modyfikacji widoku.

    Plik lists/views.py:

    from django.shortcuts import renderfrom lists.models import Item

    def home_page(request): item = Item() item.text = request.POST.get('item_text', '') item.save()

    return render(request, 'home.html', { 'new_item_text': request.POST.get('item_text', ''), })

    Przygotowa em bardzo naiwne rozwi zanie i prawdopodobnie od razu dostrze esz niezwykleoczywisty problem polegaj cy na próbie zapisu pustych elementów w trakcie ka dego daniawykonywanego wzgl dem strony g ównej. Dodajmy rozwi zanie tego problemu do naszejosobistej listy rzeczy do zrobienia. Na razie oczywiste jest, e nie mamy adnej mo liwo ciprzechowywania ró nych list dla poszczególnych osób. Spróbujmy to teraz zignorowa .

    To oczywi cie nie oznacza, e „w rzeczywistych projektach” zawsze powinni my ignorowatak ra ce problemy. Kiedy problem zostanie wcze nie dostrze ony, wtedy trzeba rozwa y ,czy przerwa dotychczas wykonywane zadanie i zacz raz jeszcze, czy jednak mo na od o-y rozwi zanie problemu na pó niej. Czasami doko czenie aktualnego zadania jest ko-

    rzystne, z kolei innym razem problem jest na tyle powa ny, e usprawiedliwia zatrzymanieprac i ponowne przemy lenie projektu.

    Zobaczmy, jaki b dzie wynik wykonania testów jednostkowych. Zaliczone! To dobrze, mo-emy przyst pi do odrobiny refaktoryzacji.

    Plik lists/views.py:

    return render(request, 'home.html', { 'new_item_text': item.text})

    Poleć książkęKup książkę

    http://helion.pl/rf/tddwprhttp://helion.pl/rt/tddwpr

  • 88 Rozdzia 5. Zapis danych wej ciowych u ytkownika

    Spójrz na nasz osobist list rzeczy do zrobienia (patrz rysunek 5.3). Doda em kilka innychproblemów, z którymi trzeba b dzie si zmierzy .

    Rysunek 5.3. Nasza osobista, uzupe niona lista rzeczy do zrobienia

    Zacznijmy od pierwszego elementu na naszej osobistej li cie. Wprawdzie mo na doda ko-lejn asercj do istniej cego testu, ale lepszym rozwi zaniem b dzie testowanie jednej rzeczyw danym momencie. Dlatego te tworzymy nowy test.

    Plik lists/tests.py:

    class HomePageTest(TestCase): [...]

    def test_home_page_only_saves_items_when_necessary(self): request = HttpRequest() home_page(request) self.assertEqual(Item.objects.count(), 0)

    Wykonanie testu ko czy si oczekiwanym niepowodzeniem, poniewa 1 != 0. Musimy topoprawi . Uwa aj, przeprowadzamy niewielk zmian w logice widoku plus kilka dodat-kowych w kodzie implementacji.

    Plik lists/views.py:

    def home_page(request): if request.method == 'POST': new_item_text = request.POST['item_text'] # Item.objects.create(text=new_item_text) # else: new_item_text = '' #

    return render(request, 'home.html', { 'new_item_text': new_item_text, # })

    U ywamy zmiennej o nazwie new_item_text, która b dzie przechowywa a zawarto -dania POST lub pusty ci g tekstowy.

    Wywo anie objects.create() to skrót pozwalaj cy na utworzenie nowego obiektu Item bezkonieczno ci wywo ania save().

    Po wprowadzonych zmianach wszystkie testy zostaj zaliczone:Ran 5 tests in 0.010sOK

    Poleć książkęKup książkę

    http://helion.pl/rf/tddwprhttp://helion.pl/rt/tddwpr

  • Przekierowanie po wykonaniu dania POST 89

    Przekierowanie po wykonaniu dania POSTFuj, rozwi zanie oparte na u yciu new_item_text = '' jest zupe nie niesatysfakcjonuj ce.Na szcz cie kolejny element naszej osobistej listy rzeczy do zrobienia daje mo liwo popra-wienia rozwi zania. Mo na si spotka ze stwierdzeniem, e nale y zawsze stosowa przekiero-wanie po wykonaniu dania POST7, a wi c tak w a nie zrobimy. Ponownie modyfikujemy nasztest jednostkowych dotycz cy zapisu danych dania POST. Zamiast wygenerowa odpowiedzawieraj c nowy element listy, zastosujemy przekierowanie z powrotem na stron g ówn .

    Plik lists/tests.py:

    def test_home_page_can_save_a_POST_request(self): request = HttpRequest() request.method = 'POST' request.POST['item_text'] = 'Nowy element listy'

    response = home_page(request)

    self.assertEqual(Item.objects.count(), 1) new_item = Item.objects.first() self.assertEqual(new_item.text, 'Nowy element listy')

    self.assertEqual(response.status_code, 302) self.assertEqual(response['location'], '/')

    Nie oczekujemy ju odpowiedzi zawieraj cej tre (content) wygenerowan przez szablon,wi c mo emy si pozby zwi zanych z ni asercji. Zamiast tego odpowied przedstawiaprzekierowanie HTTP, które powinno mie kod stanu 302 i wskazywa przegl darce interne-towej inn lokalizacj .

    W ten sposób otrzymujemy b d wynikaj cy z polecenia 200 != 302. Mo emy zdecydowanieuporz dkowa nasz widok.

    Plik lists/views.py (ch05l028):

    from django.shortcuts import redirect, renderfrom lists.models import Item

    def home_page(request): if request.method == 'POST': Item.objects.create(text=request.POST['item_text']) return redirect('/')

    return render(request, 'home.html')

    Wszystkie testy powinny zosta teraz zaliczone:Ran 5 tests in 0.010sOK

    Poszczególne testy powinny testowa pojedyncze rzeczyOmawiany widok wykonuje teraz przekierowanie po daniu POST, co jest dobr praktyk .Uda o si skróci test jednostkowy, ale nadal pozosta o miejsce na kolejne usprawnienia.W wiecie dobrych testów jednostkowych poszczególne testy powinny sprawdza jedyniepojedyncze rzeczy. To znacznie u atwia wy ledzenie b dów. Umieszczenie w te cie wielu

    7 https://en.wikipedia.org/wiki/Post/Redirect/Get

    Poleć książkęKup książkę

    http://helion.pl/rf/tddwprhttp://helion.pl/rt/tddwpr

  • 90 Rozdzia 5. Zapis danych wej ciowych u ytkownika

    asercji oznacza, e je li jedna z pierwszych spowoduje niepowodzenie, to nie b dziesz wie-dzia , jak przedstawia si stan dalszych asercji. W kolejnym rozdziale zobaczysz, e je li kie-dykolwiek przez przypadek uszkodzisz funkcjonalno widoku, to b dziesz chcia wiedzie ,czy nie dzia a zapisywanie obiektów, czy mamy nieprawid owy typ odpowiedzi.

    Nie zawsze za pierwszym razem uda si przygotowa doskona y test jednostkowy z poje-dyncz asercj , ale teraz nadesz a odpowiednia chwila na podzia omawianego testu.

    Plik lists/tests.py:

    def test_home_page_can_save_a_POST_request(self): request = HttpRequest() request.method = 'POST' request.POST['item_text'] = 'Nowy element listy'

    response = home_page(request)

    self.assertEqual(Item.objects.count(), 1) new_item = Item.objects.first() self.assertEqual(new_item.text, 'Nowy element listy')

    def test_home_page_redirects_after_POST(self): request = HttpRequest() request.method = 'POST' request.POST['item_text'] = 'Nowy element listy'

    response = home_page(request)

    self.assertEqual(response.status_code, 302) self.assertEqual(response['location'], '/')

    Teraz mamy zaliczonych sze testów zamiast pi ciu:Ran 6 tests in 0.010s

    OK

    Wygenerowanie elementów w szablonieTeraz ju znacznie lepiej! Powracamy do naszej osobistej listy rzeczy do zrobienia (patrzrysunek 5.4).

    Rysunek 5.4. Aktualna posta naszej osobistej listy rzeczy do zrobienia

    Poleć książkęKup książkę

    http://helion.pl/rf/tddwprhttp://helion.pl/rt/tddwpr

  • Wygenerowanie elementów w szablonie 91

    Skre lanie kolejnych pozycji z listy przynosi niemal tak sam satysfakcj jak obserwacja za-liczanych testów!

    Trzeci element na li cie to jednocze nie ostatni z tych „naj atwiejszych”. Przygotujemy nowytest jednostkowy sprawdzaj cy, czy szablon mo e wy wietli wiele elementów listy.

    Plik lists/tests.py:

    class HomePageTest(TestCase): [...]

    def test_home_page_displays_all_list_items(self): Item.objects.create(text='itemey 1') Item.objects.create(text='itemey 2')

    request = HttpRequest() response = home_page(request)

    self.assertIn('itemey 1', response.content.decode()) self.assertIn('itemey 2', response.content.decode())

    Wykonanie testu zgodnie z oczekiwaniem ko czy si niepowodzeniem:AssertionError: 'itemey 1' not found in '\n \n [...]

    Sk adnia szablonów Django oferuje znacznik przeznaczony do iteracji list — {% for .. in .. %}.Wymienionego znacznika mo na u y w poni szy sposób.

    Plik lists/templates/home.html:

    {% for item in items %} 1: {{ item.text }} {% endfor %}

    To jest jedna z najwi kszych zalet systemu szablonów. Teraz szablon spowoduje wygenero-wanie wielu wierszy tabeli (), po jednym dla ka dego elementu zmiennej items. Ca kiemwietnie! Wprawdzie na stronach ksi ki przedstawi jeszcze wiele innych mo liwo ci sza-

    blonów w Django, ale nadejdzie chwila, gdy b dziesz musia si gn do dokumentacji Django8

    i zapozna si z pozosta ymi.

    Modyfikacja szablonu nie powoduje zaliczenia testu. Konieczne jest rzeczywiste przekazanieszablonowi elementów z widoku strony g ównej.

    Plik lists/views.py:

    def home_page(request):