Cyfroteka.pl

klikaj i czytaj online

Cyfro
Czytomierz
00040 004961 18973060 na godz. na dobę w sumie
TDD z wykorzystaniem C# 7. Programowanie sterowane testami - książka
TDD z wykorzystaniem C# 7. Programowanie sterowane testami - książka
Autor: , Liczba stron: 360
Wydawca: Helion Język publikacji: polski
ISBN: 978-83-283-5653-5 Data wydania:
Lektor:
Kategoria: ebooki >> komputery i informatyka >> hardware >> optymalizacja wydajności
Porównaj ceny (książka, ebook (-35%), audiobook).

Coraz więcej profesjonalnych środowisk produkcyjnych opiera się na oprogramowaniu. Ewentualne błędy w pracy kodu mogą prowadzić do poważnych konsekwencji - dlatego od rozwiązań informatycznych wymaga się solidności i poprawności. Równocześnie oczekuje się wydajnego działania, skalowalności i podatności na modyfikacje, a także możliwości łatwego utrzymania kodu. Aplikacje utworzone zgodnie z paradygmatem TDD są w większym stopniu testowalne i zapewniają wysoki poziom poprawnej, stabilnej pracy. Sprawia to, że coraz więcej zespołów programistycznych skłania się ku TDD, mimo że zautomatyzowane testowanie bywa czasochłonne, pracochłonne i dość trudne w implementacji.

To książka przeznaczona dla tych, którzy chcą dogłębnie zrozumieć istotę TDD. Omówiono tu wszystkie aspekty TDD, włączając w to podstawy, dzięki którym średnio zaawansowany programista komfortowo rozpocznie budowę aplikacji zgodnie z tym paradygmatem. Przedstawiono zasady definiowania i testowania granic, a także pojęcie abstrahowania kodu zewnętrznego. W książce pojawiają się też - wprowadzane stopniowo - bardziej zaawansowane koncepcje, takie jak szpiedzy, imitacje i fałszywki. Pokazano w niej, w jaki sposób za pomocą TDD można przekształcić wymagania i historie użytkownika w funkcjonującą aplikację. Sporo miejsca poświęcono pisaniu różnych rodzajów testów, również integracyjnych. Poszczególne koncepcje zostały zilustrowane praktycznymi fragmentami kodu napisanego w C# i JavaScripcie.

W tej książce między innymi:

TDD: tak pracują najlepsi programiści!

Znajdź podobne książki Ostatnio czytane w tej kategorii

Darmowy fragment publikacji:

Tytuł oryginału: Practical Test-Driven Development using C# 7: Unleash the power of TDD by implementing real world examples under .NET environment and JavaScript Tłumaczenie: Jakub Hubisz ISBN: 978-83-283-5653-5 Copyright © Packt Publishing 2018. First published in the English language under the title ‘Practical Test-Driven Development using C# 7 – (9781788398787)’ Polish edition copyright © 2019 by Helion SA 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 Helion SA 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 Helion SA nie ponoszą również żadnej odpowiedzialności za ewentualne szkody wynikłe z wykorzystania informacji zawartych w książce. Helion SA ul. Kościuszki 1c, 44-100 Gliwice tel. 32 231 22 19, 32 230 98 63 e-mail: helion@helion.pl WWW: http://helion.pl (księgarnia internetowa, katalog książek) Drogi Czytelniku! Jeżeli chcesz ocenić tę książkę, zajrzyj pod adres http://helion.pl/user/opinie/tddwyk Moż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ść Spis tre(cid:258)ci Przedmowa O autorach O korektorze merytorycznym Wprowadzenie Rozdzia(cid:239) 1. Dlaczego TDD jest wa(cid:285)ne? Najpierw troch(cid:218) o nas Historia Johna Historia Claytona Czym jest TDD? Podej(cid:258)cie do TDD Podej(cid:258)cie alternatywne Proces Po co zawraca(cid:202) sobie tym g(cid:239)ow(cid:218)? Argumenty przeciwko TDD Testowanie wymaga czasu Testowanie jest kosztowne Testowanie jest trudne Nie wiemy jak Argumenty za TDD Mniejsza pracoch(cid:239)onno(cid:258)(cid:202) testowania manualnego Mniej b(cid:239)(cid:218)dów Pewien poziom poprawno(cid:258)ci Brak strachu przed refaktoryzacj(cid:200) Lepsza architektura Szybsza praca Ró(cid:285)ne rodzaje testów Testy jednostkowe Testy akceptacyjne 9 11 12 13 17 18 18 18 19 19 20 20 21 21 21 22 22 22 23 23 23 23 24 24 24 25 25 25 Kup książkęPoleć książkę Spis tre(cid:286)ci Testy integracyjne Testy typu end-to-end Liczba testów poszczególnych rodzajów Cz(cid:218)(cid:258)ci testu jednostkowego Aran(cid:285)acja Akcja Asercja Wymagania Dlaczego wymagania s(cid:200) wa(cid:285)ne? Historie u(cid:285)ytkownika Gherkin Nasze pierwsze testy w C# Rozwijanie aplikacji z testami Nasze pierwsze testy w JavaScripcie Dlaczego to ma znaczenie? Podsumowanie Rozdzia(cid:239) 2. Przygotowanie (cid:258)rodowiska testowego w .NET Instalacja SDK .NET Core Przygotowanie VS Code Tworzenie projektu w VS Code Przygotowanie Visual Studio Community Pobieranie Visual Studio Community Instalacja Visual Studio Community Przesiadka na xUnit Programistyczne kata Stworzenie projektu Czym jest Speaker Meet? Projekt Web API Podsumowanie Rozdzia(cid:239) 3. Przygotowanie (cid:258)rodowiska testowego w JavaScripcie Node.js Czym jest Node? Po co nam Node? Instalacja Node NPM Szybkie wprowadzenie do IDE dedykowanych dla JavaScriptu Visual Studio Code WebStorm Create React App Czym jest Create React App? Instalacja modu(cid:239)u globalnego Tworzenie aplikacji za pomoc(cid:200) Reacta Mocha i Chai Szybkie kata sprawdzaj(cid:200)ce (cid:258)rodowisko Wymagania Wykonanie Rozpocz(cid:218)cie kata Podsumowanie 4 25 26 26 26 26 26 27 27 27 27 29 31 33 34 37 37 39 39 40 44 45 46 46 46 47 47 50 51 55 57 57 58 58 58 61 62 63 64 65 66 66 66 67 72 72 72 73 76 Kup książkęPoleć książkę Rozdzia(cid:239) 4. Co nale(cid:285)y wiedzie(cid:202) przed rozpocz(cid:218)ciem pracy? Nietestowalny kod Wstrzykiwanie zale(cid:285)no(cid:258)ci Wyodr(cid:218)bnianie oprogramowania zewn(cid:218)trznego Sobowtóry testowe Frameworki imituj(cid:200)ce Zasady SOLID Powitanie zale(cid:285)ne od czasu Kruche testy Rodzaje sobowtórów testowych Przyk(cid:239)ad wielopoziomowy Podsumowanie Rozdzia(cid:239) 5. Tabula rasa — podej(cid:258)cie do aplikacji na sposób TDD Gdzie zacz(cid:200)(cid:202)? Golenie jaka Du(cid:285)y projekt od razu Czysta kartka Po jednym kawa(cid:239)ku Minimalny wykonalny produkt Inny sposób my(cid:258)lenia Nie b(cid:218)dziesz tego potrzebowa(cid:202) Ma(cid:239)e testy Adwokat diab(cid:239)a Najpierw testy (cid:258)cie(cid:285)ek negatywnych Kiedy testowanie jest bolesne Symulacja Najpierw asercja B(cid:200)d(cid:283) zorganizowany Rozbicie aplikacji Speaker Meet Prelegenci Spo(cid:239)eczno(cid:258)ci Konferencje Wymagania techniczne Podsumowanie Rozdzia(cid:239) 6. Podej(cid:258)cie do problemu Zdefiniowanie problemu Przetrawienie problemu Epiki, funkcje i historie — ojej! Problem Speaker Meet Architektura heksagonalna wielowarstwowa Architektura heksagonalna Podstawowe, ale wydajne podzia(cid:239)y wielowarstwowe Kierunek testowania Od ty(cid:239)u do przodu Od przodu do ty(cid:239)u Od wewn(cid:200)trz na zewn(cid:200)trz Podsumowanie Spis tre(cid:286)ci 77 78 78 79 79 80 80 83 84 86 93 100 101 101 102 103 103 103 104 104 104 105 106 109 113 113 114 114 114 114 115 115 115 115 117 117 118 118 120 126 127 127 130 130 137 144 148 5 Kup książkęPoleć książkę Spis tre(cid:286)ci Rozdzia(cid:239) 7. Sterowanie testami aplikacji C# Przegl(cid:200)d wymaga(cid:241) Lista prelegentów API Testy API Us(cid:239)uga Testy us(cid:239)ugi Czyste testy Repozytorium Wykorzystanie fabryki z FakeRepository Szczegó(cid:239)y prelegentów API Testy API Us(cid:239)uga Testy us(cid:239)ugi Czyste testy Co(cid:258) wi(cid:218)cej z repozytorium Dodatkowa praca zwi(cid:200)zana z fabryk(cid:200) Testowanie przypadków wyj(cid:200)tkowych Podsumowanie Rozdzia(cid:239) 8. Wyodr(cid:218)bnianie problemów na zewn(cid:200)trz Odseparowanie problemów Gravatar Planowanie na przysz(cid:239)o(cid:258)(cid:202) Abstrahowanie warstwy danych Rozszerzanie wzorca repozytorium Zapewnienie funkcji Tworzenie generycznego repozytorium Krok pierwszy: abstrahowanie interfejsu Krok drugi: abstrahowanie klasy konkretnej Krok trzeci: zmiana testów, aby wykorzystywa(cid:239)y repozytorium generyczne Entity Framework Wstrzykiwanie zale(cid:285)no(cid:258)ci Podsumowanie Rozdzia(cid:239) 9. Testowanie aplikacji napisanej w JavaScripcie Tworzenie aplikacji za pomoc(cid:200) Reacta Wyodr(cid:218)bnienie aplikacji Konfiguracja bibliotek Mocha, Chai, Enzyme i Sinon Plan Komponent React Rzut oka na testowalno(cid:258)(cid:202) Reduxa Testy jednostkowe us(cid:239)ugi API Lista prelegentów Imitacja us(cid:239)ugi API Akcja pobierania wszystkich prelegentów Reduktor dla pobierania wszystkich prelegentów Komponent listy prelegentów 6 149 149 150 150 151 156 156 160 161 163 165 165 165 169 169 172 172 173 174 176 177 177 178 185 185 186 191 210 210 211 215 219 222 223 225 226 226 226 228 228 229 230 230 231 235 239 240 Kup książkęPoleć książkę Szczegó(cid:239)y prelegenta Rozbudowa imitacji us(cid:239)ugi API Akcja pobierania prelegenta Reduktor dla pobierania prelegenta Komponent dla szczegó(cid:239)ów prelegenta Podsumowanie Rozdzia(cid:239) 10. Kwestia integracji Implementacja rzeczywistej us(cid:239)ugi API Zamiana imitacji API na prawdziw(cid:200) us(cid:239)ug(cid:218) Wykorzystanie biblioteki Sinon do imitacji odpowiedzi Ajaxa Konfiguracja aplikacji Testy integracyjne od pocz(cid:200)tku do ko(cid:241)ca Zalety Wady Ile testów end-to-end trzeba mie(cid:202)? Konfiguracja API Projekt dla testów integracyjnych Gdzie zacz(cid:200)(cid:202)? Weryfikacja odwo(cid:239)a(cid:241) repozytorium do bazy Weryfikacja, czy us(cid:239)uga odwo(cid:239)uje si(cid:218) do bazy przez repozytorium Weryfikacja odwo(cid:239)a(cid:241) API do us(cid:239)ugi Podsumowanie Rozdzia(cid:239) 11. Zmiany w wymaganiach Witaj, (cid:258)wiecie Zmiana wymaga(cid:241) FizzBuzz Nowa funkcja Aplikacja TODO Oznaczanie jako wykonane Dodanie testów Kod produkcyjny Dodanie testów Kod produkcyjny Zmiany w aplikacji Speaker Meet Zmiany po stronie serwera Zmiany po stronie interfejsu Co teraz? Przedwczesna optymalizacja Podsumowanie Rozdzia(cid:239) 12. Problem z kodem zastanym Czym jest kod zastany? Dlaczego kod staje si(cid:218) z(cid:239)y? Kiedy projekt staje si(cid:218) projektem zastanym? Co mo(cid:285)emy zrobi(cid:202), aby zapobiec powstawaniu kodu zastanego? Typowe problemy wynikaj(cid:200)ce z kodu zastanego Niezamierzone skutki uboczne Nadmierna optymalizacja Spis tre(cid:286)ci 246 246 248 254 257 260 261 261 262 264 273 273 273 274 274 274 275 275 275 278 280 284 285 286 286 287 287 289 289 289 290 290 292 293 293 295 296 296 297 299 299 300 300 301 302 302 303 7 Kup książkęPoleć książkę Spis tre(cid:286)ci Zbyt sprytny kod (cid:165)cis(cid:239)e (cid:239)(cid:200)czenie z kodem zewn(cid:218)trznym Problemy przeszkadzaj(cid:200)ce w dodawaniu testów Bezpo(cid:258)rednia zale(cid:285)no(cid:258)(cid:202) od frameworka lub kodu zewn(cid:218)trznego Prawo Demeter Praca w konstruktorze Globalny stan Metody statyczne Du(cid:285)e klasy i funkcje Radzenie sobie z problemami wynikaj(cid:200)cymi z kodu zastanego Bezpieczna refaktoryzacja Pierwsze testy Id(cid:200)c dalej Naprawa b(cid:239)(cid:218)dów Niebezpieczna refaktoryzacja Podsumowanie Rozdzia(cid:239) 13. Sprz(cid:200)tanie ba(cid:239)aganu Dziedziczenie kodu Gra Pro(cid:258)ba o zmian(cid:218) Czasami dostajesz od (cid:285)ycia cytryny Zaczynamy Abstrakcja klasy zewn(cid:218)trznej Niespodziewane dane wsadowe Szukanie sensu w szale(cid:241)stwie Ko(cid:241)cowe upi(cid:218)kszanie Gotowy na ulepszenia Podsumowanie Rozdzia(cid:239) 14. Poka(cid:285) si(cid:218) z najlepszej strony Co omówili(cid:258)my? Id(cid:200)c naprzód TDD to osobisty wybór Nie potrzebujesz pozwolenia Rozwijaj aplikacje poprzez testy Wprowadzanie TDD do Twojego zespo(cid:239)u Nie zmuszaj nikogo do TDD Grywalizacja TDD Poka(cid:285) zespo(cid:239)owi zalety Kontroluj rezultaty Powrót do (cid:258)wiata jako ekspert TDD Poszukaj mentora Zosta(cid:241) mentorem Praktykuj, praktykuj, praktykuj Podsumowanie Skorowidz 8 304 304 305 305 306 306 307 307 307 308 308 310 312 313 313 313 315 315 316 316 317 317 320 324 329 335 337 349 351 351 352 352 353 353 353 354 354 354 355 355 355 356 356 357 358 Kup książkęPoleć książkę 4 Co nale(cid:285)y wiedzie(cid:202) przed rozpocz(cid:218)ciem pracy? Ca(cid:239)kiem nie(cid:283)le zacz(cid:200)(cid:239)e(cid:258). Powiniene(cid:258) ju(cid:285) zaczyna(cid:202) czu(cid:202) si(cid:218) komfortowo z podstawowymi koncepcjami programowania sterowanego testami. Znasz ju(cid:285) podstawowe za(cid:239)o(cid:285)enia TDD i wiesz, jak pisa(cid:202) testy jednostkowe w C# i JavaScripcie. W tym rozdziale: (cid:81) Omówimy wi(cid:218)cej praktyk zwi(cid:200)zanych z TDD (cid:81) Przeka(cid:285)emy porady, jak unikn(cid:200)(cid:202) pu(cid:239)apek czyhaj(cid:200)cych na nowych praktyków TDD (cid:81) Obja(cid:258)nimy, dlaczego wa(cid:285)ne jest okre(cid:258)lenie granic testowania, wyodr(cid:218)bniaj(cid:200)c kod zewn(cid:218)trzny (w(cid:239)(cid:200)cznie z frameworkiem .NET) (cid:81) Zaczniemy wprowadza(cid:202) bardziej zaawansowane koncepcje, takie jak szpiegi, imitacje i fa(cid:239)szywki Najpierw omówimy problemy, jakie mo(cid:285)esz napotka(cid:202), próbuj(cid:200)c stworzy(cid:202) testy dla istniej(cid:200)- cych aplikacji. Mamy nadziej(cid:218), (cid:285)e pomo(cid:285)e Ci to unikn(cid:200)(cid:202) tego typu problemów podczas two- rzenia od podstaw swojej kolejnej aplikacji. Kup książkęPoleć książkę TDD w wykorzystaniem C# 7 Nietestowalny kod Istnieje wiele oznak pozwalaj(cid:200)cych przypuszcza(cid:202), (cid:285)e aplikacja, klasa lub metoda b(cid:218)d(cid:200) trud- ne lub nawet niemo(cid:285)liwe do przetestowania. Oczywi(cid:258)cie istniej(cid:200) sposoby na obej(cid:258)cie niektó- rych z poni(cid:285)szych przyk(cid:239)adów, ale z regu(cid:239)y najlepiej unika(cid:202) obej(cid:258)(cid:202) i programistycznej akro- batyki. Proste rozwi(cid:200)zania s(cid:200) zwykle najlepsze, a osoby, które b(cid:218)d(cid:200) musia(cid:239)y utrzymywa(cid:202) Twoj(cid:200) aplikacj(cid:218), podzi(cid:218)kuj(cid:200) Ci za trzymanie si(cid:218) prostoty (albo sam sobie za to podzi(cid:218)kujesz). Wstrzykiwanie zale(cid:285)no(cid:258)ci Je(cid:285)eli tworzysz instancje zewn(cid:218)trznych zasobów wewn(cid:200)trz konstruktorów lub wewn(cid:200)trz metod zamiast przekazywa(cid:202) je do nich, bardzo ci(cid:218)(cid:285)ko b(cid:218)dzie napisa(cid:202) testy pokrywaj(cid:200)ce te klasy czy metody. We wspó(cid:239)czesnych aplikacjach do tworzenia i przekazywania do klas ze- wn(cid:218)trznych zale(cid:285)no(cid:258)ci wykorzystywane s(cid:200) frameworki wstrzykiwania zale(cid:285)no(cid:258)ci. Wiele osób decyduje si(cid:218) na zdefiniowanie interfejsu jako kontraktu dla zale(cid:285)no(cid:258)ci, co zapewnia wi(cid:218)ksz(cid:200) elastyczno(cid:258)(cid:202) na potrzeby testów w (cid:239)(cid:200)czeniu zewn(cid:218)trznych zasobów. Elementy statyczne By(cid:202) mo(cid:285)e b(cid:218)dziesz musia(cid:239) odwo(cid:239)ywa(cid:202) si(cid:218) do zewn(cid:218)trznych statycznych klas i metod. Zamiast bezpo(cid:258)rednio odwo(cid:239)ywa(cid:202) si(cid:218) do nich lepiej uzyskiwa(cid:202) do nich dost(cid:218)p przez interfejs. Na przy- k(cid:239)ad w(cid:239)a(cid:258)ciwo(cid:258)(cid:202) Now klasy DateTime jest statyczna, co nie pozwala Ci kontrolowa(cid:202) warto(cid:258)ci DateTime u(cid:285)ywanej przez testowan(cid:200) klas(cid:218) czy metod(cid:218). To utrudnia weryfikacj(cid:218) przypadków testowych i zapewnienie poprawnego zachowania si(cid:218) logiki programu. Singleton Singletony stanowi(cid:200) esencj(cid:218) wspó(cid:239)dzielonego stanu. Aby zapewni(cid:202) uruchamianie testu w izolowanym (cid:258)rodowisku, najlepiej ich unika(cid:202). Je(cid:285)eli singleton jest wymagany (na przyk(cid:239)ad na potrzeby logowania czy kontekstu danych), wi(cid:218)kszo(cid:258)(cid:202) frameworków wstrzykiwania za- le(cid:285)no(cid:258)ci umo(cid:285)liwia podmian(cid:218) klasy nie b(cid:218)d(cid:200)cej singletonem na jedn(cid:200) instancj(cid:218), co w efekcie daje funkcjonalno(cid:258)(cid:202) i elastyczno(cid:258)(cid:202) singletonu. W kodzie produkcyjnym pozwala to kontrolowa(cid:202) zakres instancji singletonu. Stan globalny Od dawna wiadomo, (cid:285)e globalny stan w aplikacji powoduje ogromne zamieszanie w syste- mie i nieoczekiwane zachowanie, które mo(cid:285)e by(cid:202) trudne do wykrycia. Zmiana kodu w jed- nym miejscu mo(cid:285)e mie(cid:202) daleko id(cid:200)ce efekty uboczne w innych cz(cid:218)(cid:258)ciach systemu. Dla te- stowalno(cid:258)ci oznacza to cz(cid:218)sto zwi(cid:218)kszon(cid:200) trudno(cid:258)(cid:202) w przygotowywaniu testów i wolniejsze ich wykonywanie. 78 Kup książkęPoleć książkę Rozdzia(cid:225) 4. • Co nale(cid:298)y wiedzie(cid:252) przed rozpocz(cid:266)ciem pracy? Wyodr(cid:218)bnianie oprogramowania zewn(cid:218)trznego Wraz z rozbudow(cid:200) aplikacji b(cid:218)dziesz prawdopodobnie wprowadza(cid:202) zale(cid:285)no(cid:258)ci zewn(cid:218)trzne. Z pewno(cid:258)ci(cid:200) twórcy tych systemów, aplikacji i bibliotek dok(cid:239)adnie przetestowali swoje roz- wi(cid:200)zania. Powiniene(cid:258) skupi(cid:202) si(cid:218) na testowaniu swojego kodu, nie kodu zewn(cid:218)trznego. Twoja aplikacja powinna by(cid:202) na tyle solidna, aby obs(cid:239)u(cid:285)y(cid:202) warunki brzegowe, i musisz bra(cid:202) pod uwag(cid:218) oczekiwane i nieoczekiwane zachowanie. Szczegó(cid:239)y kodu zewn(cid:218)trznego nale(cid:285)y wy- odr(cid:218)bni(cid:202), aby mo(cid:285)na by(cid:239)o przetestowa(cid:202) oczekiwane (i nieoczekiwane) zachowanie. Czym jest kod zewn(cid:218)trzny? Wszystkim, co nie zosta(cid:239)o napisane przez Ciebie. Wlicza si(cid:218) w to tak(cid:285)e .NET Framework. Jednym ze sposobów wyodr(cid:218)bnienia kodu zewn(cid:218)trznego s(cid:200) sobowtóry testowe. Sobowtóry testowe Sobowtóry testowe to funkcje i klasy pomocnicze w procesie testowania, pomagaj(cid:200)ce zwe- ryfikowa(cid:202) funkcj(cid:218) lub omin(cid:200)(cid:202) zale(cid:285)no(cid:258)(cid:202), która mog(cid:239)aby by(cid:202) trudna do przetestowania. Sobowtóry testowe s(cid:200) u(cid:285)ywane na wszystkich poziomach do izolowania testowanego kodu. W wielu przypadkach konieczno(cid:258)(cid:202) posiadania sobowtórów testowych ma decyduj(cid:200)cy wp(cid:239)yw na architektur(cid:218) kodu. Przyk(cid:239)adem jest obiekt DateTime w C#. System.DateTime jest cz(cid:218)(cid:258)ci(cid:200) frameworka .NET i normalnie nie bra(cid:239)by(cid:258) pod uwag(cid:218) wyodr(cid:218)bniania go z kodu. Instynkt wi(cid:218)kszo(cid:258)ci programi- stów podpowiada im, aby doda(cid:202) referencj(cid:218) za pomoc(cid:200) polecenia using i bezpo(cid:258)rednio od- wo(cid:239)ywa(cid:202) si(cid:218) do niego w kodzie. Test, który nie mo(cid:285)e zosta(cid:202) powtórzony, jest z(cid:239)ym testem. Cz(cid:218)sto trudno przetestowa(cid:202) taki kod. Je(cid:285)eli chcieliby(cid:258)my przetestowa(cid:202) metod(cid:218) u(cid:285)ywaj(cid:200)c(cid:200) w(cid:239)a(cid:258)ciwo(cid:258)ci DateTime.Now, nie byliby(cid:258)my w stanie zapobiec wykonaniu domy(cid:258)lnego zacho- wania DateTime.Now. DateTime.Now zwraca aktualn(cid:200) dat(cid:218) i czas w obiekcie DateTime. Brak wp(cid:239)ywu na to, jak(cid:200) warto(cid:258)(cid:202) zwraca obiekt, powoduje, (cid:285)e testy b(cid:218)d(cid:200) nieprzewidywalne i nie- powtarzalne. Test, który nie mo(cid:285)e by(cid:202) powtórzony, jest z(cid:239)ym testem. Wielu programistów rozumie potrzeb(cid:218) przewidywalno(cid:258)ci. By(cid:202) mo(cid:285)e s(cid:239)ysza(cid:239)e(cid:258) powiedzenie: „Je(cid:285)eli nie mo(cid:285)na tego zreprodukowa(cid:202), nie jest to b(cid:239)(cid:200)d”. To dlatego, (cid:285)e aby zweryfikowa(cid:202), czy uda(cid:239)o nam si(cid:218) poprawi(cid:202) b(cid:239)(cid:200)d, musimy by(cid:202) w stanie zreprodukowa(cid:202) go w sposób przewi- dywalny. W ten sposób, kiedy kroki prowadz(cid:200)ce do wyst(cid:200)pienia b(cid:239)(cid:218)du ju(cid:285) go nie powoduj(cid:200), mo(cid:285)emy bezpiecznie stwierdzi(cid:202), (cid:285)e zosta(cid:239) poprawiony. Przynajmniej dla tej sekwencji kroków. Testowanie nie ró(cid:285)ni si(cid:218) specjalnie od naprawiania b(cid:239)(cid:218)dów; wykona(cid:202) nale(cid:285)y te same kroki. Wiemy po prostu dok(cid:239)adnie, co spowodowa(cid:239)o b(cid:239)(cid:200)d — kod nie zosta(cid:239) jeszcze napisany lub co(cid:258) posz(cid:239)o nie tak podczas refaktoringu. 79 Kup książkęPoleć książkę TDD w wykorzystaniem C# 7 Tworzenie sobowtórów testowych mo(cid:285)e czasami by(cid:202) mocno anga(cid:285)uj(cid:200)ce. Z tego powodu w pra- wie ka(cid:285)dym j(cid:218)zyku wspieraj(cid:200)cym testowanie stworzono frameworki pomagaj(cid:200)ce w ich two- rzeniu. Nazywa si(cid:218) je frameworkami lub bibliotekami imituj(cid:200)cymi. Najcz(cid:218)(cid:258)ciej stosowany framework w C# to Moq. W JavaScripcie najcz(cid:218)(cid:258)ciej pobieran(cid:200) bibliotek(cid:200) imituj(cid:200)c(cid:200) jest Sinon. Frameworki imituj(cid:200)ce Frameworki imituj(cid:200)ce to doskona(cid:239)e narz(cid:218)dzia pomagaj(cid:200)ce z(cid:239)agodzi(cid:202) nieco trudy testowania w du(cid:285)ym projekcie. S(cid:200) szczególnie u(cid:285)yteczne podczas prób zbudowania testów dla syste- mów zastanych. System zastany jest w tym przypadku definiowany jako aplikacja, która nie posiada testów automatycznych. Definicja ta pochodzi z ksi(cid:200)(cid:285)ki Michaela Feathera pod ty- tu(cid:239)em Praca z zastanym kodem. Musisz zachowa(cid:202) czujno(cid:258)(cid:202) podczas nauki TDD i stosowania frameworków imituj(cid:200)cych. Frameworki imituj(cid:200)ce stanowi(cid:200) bardzo atrakcyjn(cid:200) alternatyw(cid:218) dla szczegó(cid:239)owego analizowania Twojego kodu. Mo(cid:285)liwe jest napisanie kompletnego zestawu testów, które w ko(cid:241)cowym roz- rachunku testuj(cid:200) tylko framework imituj(cid:200)cy. Wiele z tych frameworków jest pod tym wzgl(cid:218)dem zbyt pot(cid:218)(cid:285)nych. W C# istniej(cid:200) frame- worki imituj(cid:200)ce, które pozwalaj(cid:200) podmienia(cid:202) kod zewn(cid:218)trzny. W(cid:239)(cid:200)cza si(cid:218) w to DateTime.Now i ka(cid:285)da inna klasa, której nie kontrolujesz. W JavaScripcie nazywa si(cid:218) to ma(cid:239)pim (cid:239)ataniem i ka(cid:285)dy framework na to pozwala. Pytasz, co w tym z(cid:239)ego. Jedn(cid:200) z zalet TDD jest to, (cid:285)e zach(cid:218)ca do m(cid:200)drych wyborów archi- tektonicznych. Kiedy masz mo(cid:285)liwo(cid:258)(cid:202) nadpisa(cid:202) funkcje zewn(cid:218)trznego kodu, tracisz potrze- b(cid:218) wyodr(cid:218)bniania na potrzeby testów. Dlaczego jest to problemem? Wyodr(cid:218)bnienie kodu zewn(cid:218)trznego jest niezb(cid:218)dne, je(cid:285)eli chcemy, aby kod by(cid:239) elastyczny i je(cid:285)eli chcemy przestrzega(cid:202) zasad SOLID. Zasady SOLID Zasady SOLID to zestaw koncepcji zgrupowanych przez Roberta C. Martina, zwanego rów- nie(cid:285) wujkiem Bobem. Opisywane s(cid:200) zwykle jako zasady programowania zorientowanego obiektowo, ale my(cid:258)l o nich raczej jak o dobrych wyborach architektonicznych. SOLID sk(cid:239)a- da si(cid:218) z pi(cid:218)ciu zasad: zasady jednej odpowiedzialno(cid:258)ci (ang. Single Responsibility), zasady otwarte – zamkni(cid:218)te (ang. Open/Closed), zasady podstawienia Liskov (ang. Liskov Substitution), zasady segregacji interfejsów (ang. Interface Segregation) i zasady odwrócenia zale(cid:285)no(cid:258)ci (ang. Dependency Inversion). Artyku(cid:239)y przedstawiaj(cid:200)ce zasady SOLID znajdziesz pod adresem http://butunclebob.com/ ArticleS.UncleBob.PrinciplesOfOod. 80 Kup książkęPoleć książkę Rozdzia(cid:225) 4. • Co nale(cid:298)y wiedzie(cid:252) przed rozpocz(cid:266)ciem pracy? Zasada jednej odpowiedzialno(cid:258)ci W artykule wujka Boba zasada jednej odpowiedzialno(cid:258)ci jest zdefiniowana nast(cid:218)puj(cid:200)co: „Klasa powinna mie(cid:202) tylko jeden powód do zmiany”. Co to oznacza? To trudna kwestia i mo(cid:285)na j(cid:200) pojmowa(cid:202) na wiele sposobów. Jeden z nich mówi, (cid:285)e klasa powinna mie(cid:202) tylko jednego u(cid:285)ytkownika biznesowego. Inny, (cid:285)e w ramach aplikacji klasa powinna by(cid:202) wykorzystywana tylko w ograniczonym lub bardzo konkretnym zakresie. Jeszcze inny, (cid:285)e klasa powinna mie(cid:202) ograniczon(cid:200) funkcjonalno(cid:258)(cid:202). Wszystkie te spo- soby s(cid:200) poprawne, jednak wci(cid:200)(cid:285) niewystarczaj(cid:200)ce. Jednym ze sposobów zapewnienia prze- strzegania regu(cid:239)y jednej odpowiedzialno(cid:258)ci jest wykorzystanie regu(cid:239)y nazywanej przez nas „trzy do pi(cid:218)ciu”. Je(cid:285)eli na przyk(cid:239)ad omawiamy wymagania, to kiedy wymaganie ma trzy do pi(cid:218)ciu kryteriów akceptacji, najprawdopodobniej jest to liczba w(cid:239)a(cid:258)ciwa dla jego poziomu szczegó(cid:239)owo(cid:258)ci. Analogicznie w przypadku metody lub funkcji trzy do pi(cid:218)ciu linii kodu to prawdopodobnie odpowiedni rozmiar. Regu(cid:239)a „trzy do pi(cid:218)ciu” to ogólny sposób okre(cid:258)lenia, czy przestrzegasz zasady jednej odpo- wiedzialno(cid:258)ci. Regu(cid:239)a mówi: „Mniej ni(cid:285) trzy to dobrze, pomi(cid:218)dzy trzy a pi(cid:218)(cid:202) jest akcepto- walnie, powy(cid:285)ej pi(cid:218)ciu nale(cid:285)y rozwa(cid:285)y(cid:202) refaktoryzacj(cid:218)”. Regu(cid:239)a nie jest mo(cid:285)e tak elegancka jak wiele innych praw, zasad czy regu(cid:239), ale za to (cid:239)atwo jej przestrzega(cid:202). Nie powinna jednak by(cid:202) stosowana tylko w ostateczno(cid:258)ci. Staraj si(cid:218) stosowa(cid:202) j(cid:200) do prawie wszystkich etapów wytwarzania oprogramowania. Widzia(cid:239)e(cid:258) j(cid:200) ju(cid:285) zreszt(cid:200) w akcji w tej ksi(cid:200)(cid:285)ce — zosta(cid:239)a wyko- rzystana do okre(cid:258)lenia zakresu wymaga(cid:241) w rozdziale 1., „Dlaczego TDD jest wa(cid:285)ne?”, oraz we wszystkich dotychczasowych przyk(cid:239)adach kodu. Je(cid:285)eli b(cid:218)dziesz korzysta(cid:202) z regu(cid:239)y „trzy do pi(cid:218)ciu”, mog(cid:218) niemal(cid:285)e zagwarantowa(cid:202), i(cid:285) b(cid:218)dziesz przestrzega(cid:202) regu(cid:239)y jednej odpowiedzialno(cid:258)ci. Dzi(cid:218)ki temu równie(cid:285) Twój kod, struktura plików i wymagania b(cid:218)d(cid:200) niewielkie i (cid:239)atwe w utrzymaniu. Zasada otwarte – zamkni(cid:218)te Zasada otwarte – zamkni(cid:218)te mówi: „Encje oprogramowania (klasy, modu(cid:239)y, funkcje itd.) po- winny by(cid:202) otwarte na rozszerzanie, ale zamkni(cid:218)te na modyfikacje”. Druga zasada SOLID zdaje si(cid:218) nie mówi(cid:202) zbyt wiele, ale ma bardzo du(cid:285)y wp(cid:239)yw na struktur(cid:218) kodu. Jest wiele sposobów, by przestrzega(cid:202) tej zasady. Ty lub Twój zespó(cid:239) programistyczny mo(cid:285)e- cie wprowadzi(cid:202) w (cid:285)ycie regu(cid:239)(cid:218) pozwalaj(cid:200)c(cid:200) na pisanie wy(cid:239)(cid:200)cznie nowego kodu. Oznacza to, (cid:285)e (cid:285)adna z istniej(cid:200)cych funkcji programu nie mo(cid:285)e by(cid:202) aktualizowana czy modyfikowana — mo(cid:285)liwe jest jedynie zast(cid:200)pienie jej now(cid:200) funkcj(cid:200). Kiedy dotrzemy w kodzie do miejsca, gdzie nast(cid:218)puje podzia(cid:239) na star(cid:200) i now(cid:200) funkcj(cid:218), mo(cid:285)emy dokona(cid:202) podmiany. Zasada otwarte – zamkni(cid:218)te sprzyja równie(cid:285) ci(cid:200)g(cid:239)ej integracji i ci(cid:200)g(cid:239)emu dostarczaniu. Jest tak dlatego, (cid:285)e je(cid:285)eli Twoja aplikacja nigdy nie z(cid:239)amie kontraktu z u(cid:285)ytkownikiem, sam(cid:200) sob(cid:200) czy zewn(cid:218)trznym oprogramowaniem, zawsze mo(cid:285)na j(cid:200) umie(cid:258)ci(cid:202) w (cid:258)rodowisku produkcyjnym bez obawy, (cid:285)e pojawi(cid:200) si(cid:218) jakie(cid:258) problemy. 81 Kup książkęPoleć książkę TDD w wykorzystaniem C# 7 Zasada podstawienia Liskov Zasada podstawienia Liskov mo(cid:285)e by(cid:202) trudna do zrozumienia przy pierwszym podej(cid:258)ciu, poniewa(cid:285) jej definicja jest do(cid:258)(cid:202) skomplikowana i matematyczna. Barbara Liskov w pracy Data Abstraction and Hierarchy (https://pdfs.semanticscholar.org/36be/babeb72287ad9490e1 (cid:180)ebab84e7225ad6a9e5.pdf) opisa(cid:239)a zasad(cid:218) nast(cid:218)puj(cid:200)co: Chodzi nam o osi(cid:200)gni(cid:218)cie podstawienia podobnego do nast(cid:218)puj(cid:200)cego: je(cid:285)eli dla ka(cid:285)de- go obiektu o1 typu S istnieje obiekt o2 typu T taki, (cid:285)e dla wszystkich programów P zdefiniowanych w ramach warunków T zachowanie P pozostaje niezmienione, kiedy o1 zostanie zast(cid:200)pione przez o2, wtedy S jest podtypem T. Wujek Bob upro(cid:258)ci(cid:239) definicj(cid:218) w nast(cid:218)puj(cid:200)cy sposób: „Funkcje u(cid:285)ywaj(cid:200)ce wska(cid:283)ników lub referencji do klas bazowych musz(cid:200) by(cid:202) w stanie korzysta(cid:202) z obiektów klas pochodnych, nie maj(cid:200)c o nich wiedzy”. Patrz(cid:200)c na t(cid:218) zasad(cid:218), wydaje si(cid:218), (cid:285)e chodzi tylko o dziedziczenie. Tak jednak nie jest. Zasada ta oznacza, (cid:285)e obiekt zast(cid:218)puj(cid:200)cy inny obiekt nie tylko musi imple- mentowa(cid:202) ten sam interfejs lub kontrakt co obiekt oryginalny; musi równie(cid:285) spe(cid:239)nia(cid:202) te same oczekiwania co orygina(cid:239). Klasycznym przyk(cid:239)adem pogwa(cid:239)cenia tej zasady jest u(cid:285)ycie klasy kwadrat w miejsce klasy pro- stok(cid:200)t. Typowa klasa prostok(cid:200)t musi mie(cid:202) w(cid:239)a(cid:258)ciwo(cid:258)ci szeroko(cid:258)(cid:202) i wysoko(cid:258)(cid:202). W matematyce kwadrat jest specyficznym rodzajem prostok(cid:200)ta. Wiele osób zak(cid:239)ada wi(cid:218)c, (cid:285)e stworzenie klasy kwadrat z szeroko(cid:258)ci(cid:200) i wysoko(cid:258)ci(cid:200) by(cid:239)oby akceptowalnym substytutem dla klasy prostok(cid:200)t. Problem polega na tym, (cid:285)e kwadrat wymaga, aby szeroko(cid:258)(cid:202) i wysoko(cid:258)(cid:202) by(cid:239)y identyczne. Je(cid:285)eli wi(cid:218)c zmienisz jedn(cid:200) z w(cid:239)a(cid:258)ciwo(cid:258)ci klasy kwadrat, klasa zaktualizuje drug(cid:200) t(cid:200) sam(cid:200) warto- (cid:258)ci(cid:200). Jest to problem, poniewa(cid:285) aplikacja korzystaj(cid:200)ca z obiektu nie spodziewa si(cid:218) takiego zachowania. Aplikacja musi wi(cid:218)c mie(cid:202) wiedz(cid:218), (cid:285)e d(cid:239)ugo(cid:258)(cid:202) lub wysoko(cid:258)(cid:202) mo(cid:285)e si(cid:218) zmieni(cid:202) bez ostrze(cid:285)enia. Niemo(cid:285)no(cid:258)(cid:202) spe(cid:239)nienia oczekiwa(cid:241) aplikacji nazywa si(cid:218) odrzuconym (cid:285)(cid:200)daniem. Odrzucone (cid:285)(cid:200)danie mo(cid:285)e powodowa(cid:202) niespójne zachowanie aplikacji i wymaga przynajmniej wi(cid:218)kszej ilo(cid:258)ci kodu do skompensowania niezgodno(cid:258)ci. Zasada segregacji interfejsów Zasada segregacji interfejsów dotyczy utrzymania niewielkich rozmiarów kontraktów interakcji klasy. Kontrakty powinny by(cid:202) jednak nie tylko niewielkie, ale te(cid:285) mie(cid:202) jedn(cid:200) odpowiedzialno(cid:258)(cid:202). Czasami klasa z niewielkim lub maj(cid:200)cym jedn(cid:200) odpowiedzialno(cid:258)(cid:202) kontraktem jest niemo(cid:285)li- wa do osi(cid:200)gni(cid:218)cia lub niepo(cid:285)(cid:200)dana. W takich przypadkach powinna ona implementowa(cid:202) wiele kontraktów zamiast jednego po(cid:239)(cid:200)czonego. Chcemy mie(cid:202) wiele kontraktów, aby zredu- kowa(cid:202) liczb(cid:218) daleko si(cid:218)gaj(cid:200)cych zale(cid:285)no(cid:258)ci. Ilekro(cid:202) klasa bazowa lub interfejs s(cid:200) modyfikowane, klasy pochodne równie(cid:285) wymagaj(cid:200) zmian. W najlepszym przypadku musz(cid:200) zosta(cid:202) przekompilowane. Ograniczaj(cid:200)c zakres kon- traktu, mo(cid:285)emy ograniczy(cid:202) wp(cid:239)yw zmian wprowadzanych w tym kontrakcie i poprawi(cid:202) jako(cid:258)(cid:202) ogólnej architektury systemu. 82 Kup książkęPoleć książkę Rozdzia(cid:225) 4. • Co nale(cid:298)y wiedzie(cid:252) przed rozpocz(cid:266)ciem pracy? Zasada odwrócenia zale(cid:285)no(cid:258)ci Odwrócenie zale(cid:285)no(cid:258)ci jest wa(cid:285)ne z kilku powodów, mi(cid:218)dzy innymi dlatego, (cid:285)e powoduje zwi(cid:218)kszenie elastyczno(cid:258)ci kodu, zmniejszenie jego podatno(cid:258)ci na b(cid:239)(cid:218)dy i zwi(cid:218)kszenie mo(cid:285)- liwo(cid:258)ci wielokrotnego u(cid:285)ycia kodu. Odwrócenie zale(cid:285)no(cid:258)ci pomaga w osi(cid:200)gni(cid:218)ciu architektury opartej na wtyczkach. Definiuj(cid:200)c kontrakt interakcji, modu(cid:239) mo(cid:285)e okre(cid:258)li(cid:202), jak chce podejmowa(cid:202) interakcje z zale(cid:285)no(cid:258)ciami. W ten sposób zale(cid:285)no(cid:258)ci uzale(cid:285)niaj(cid:200) si(cid:218) od kontraktu. Poniewa(cid:285) modu(cid:239) na najwy(cid:285)szym poziomie nie ma zale(cid:285)no(cid:258)ci wychodz(cid:200)cych, mo(cid:285)e by(cid:202) wdra(cid:285)any niezale(cid:285)nie. Wdra(cid:285)anie produkcyjne cz(cid:218)(cid:258)ci aplikacji prawie nigdy si(cid:218) nie zdarza, ale posiadanie tak niezale(cid:285)nej biblioteki daje ogromn(cid:200) wygod(cid:218) w postaci braku konieczno(cid:258)ci rekompilacji, kiedy zmieniane s(cid:200) zale(cid:285)no(cid:258)ci. Podczas standardowego wytwarzania oprogramowania zale(cid:285)no(cid:258)ci zmieniaj(cid:200) si(cid:218) znacznie cz(cid:218)(cid:258)ciej ni(cid:285) modu(cid:239)y wysokiego poziomu. Zmiany te powoduj(cid:200) konieczno(cid:258)(cid:202) rekompilacji. Kiedy zale(cid:285)no(cid:258)ci w aplikacji sp(cid:239)ywaj(cid:200) w dó(cid:239) hierarchii, rekompilacja zale(cid:285)no(cid:258)ci uruchamia równie(cid:285) rekompilacj(cid:218) biblioteki od niej zale(cid:285)nej. W efekcie zmiana klasy pomocniczej w nie- wielkiej, ale cz(cid:218)sto wykorzystywanej bibliotece spowoduje rekompilacj(cid:218) ca(cid:239)ej aplikacji. Je(cid:285)eli jednak odwracasz zale(cid:285)no(cid:258)ci, tego typu zmiana wywo(cid:239)a tylko konieczno(cid:258)(cid:202) rekompila- cji biblioteki zawieraj(cid:200)cej t(cid:218) klas(cid:218) pomocnicz(cid:200) i biblioteki aplikacji. Nie b(cid:218)dzie wymaga(cid:239)a rekompilacji wszystkich bibliotek po(cid:258)rednich. To tyle, je(cid:258)li chodzi o zasady SOLID. Pami(cid:218)taj o nich podczas wyboru biblioteki imituj(cid:200)cej. Upewnij si(cid:218), (cid:285)e biblioteka imituj(cid:200)ca nie sk(cid:239)oni Ci(cid:218) do budowy sztywnego, wra(cid:285)liwego i trud- nego w utrzymaniu systemu. Powitanie zale(cid:285)ne od czasu Rozbudujmy nieco klasyczny przyk(cid:239)ad „Witaj, (cid:258)wiecie”. Mo(cid:285)e by tak wy(cid:258)wietla(cid:202) powitanie uzale(cid:285)nione od pory dnia? Przyk(cid:239)ad jest nast(cid:218)puj(cid:200)cy: Jako odwiedzaj(cid:200)cy stron(cid:218) Chc(cid:218) otrzyma(cid:202) powitanie odpowiednie dla aktualnej pory dnia Aby móc zaplanowa(cid:202) zg(cid:239)aszanie moich prelekcji Zak(cid:239)adaj(cid:200)c (cid:285)e jest przed godzin(cid:200) 18 Kiedy (cid:285)(cid:200)dane jest powitanie Wtedy otrzymam powitanie dzienne Zak(cid:239)adaj(cid:200)c (cid:285)e jest po godzinie 18 Kiedy (cid:285)(cid:200)dane jest powitanie Wtedy otrzymam powitanie wieczorne By(cid:202) mo(cid:285)e pomy(cid:258)la(cid:239)e(cid:258) sobie: „To proste, wystarczy, (cid:285)e szybko sklec(cid:218) metod(cid:218) zwracaj(cid:200)c(cid:200) od- powiedni(cid:200) wiadomo(cid:258)(cid:202)”. Oczywi(cid:258)cie mia(cid:239)by(cid:258) racj(cid:218). To do(cid:258)(cid:202) proste zadanie. Móg(cid:239)by(cid:258) napisa(cid:202) co(cid:258) takiego: 83 Kup książkęPoleć książkę TDD w wykorzystaniem C# 7 public string GetGreeting() { if (DateTime.Now.Hour 18) return Dzie(cid:241) dobry ; return Dobry wieczór ; } W rozdziale 1., „Dlaczego TDD jest wa(cid:285)ne?”, omawiali(cid:258)my trzy prawa TDD. Pierwsze pra- wo mówi, (cid:285)e nie wolno napisa(cid:202) ani jednej linii kodu, nie utworzywszy testu ko(cid:241)cz(cid:200)cego si(cid:218) niepowodzeniem. Kruche testy Mo(cid:285)esz powiedzie(cid:202), (cid:285)e przecie(cid:285) to jest tak banalna metoda. A gdyby(cid:258) napotka(cid:239) b(cid:239)(cid:200)d? Albo gdyby(cid:258) chcia(cid:239) napisa(cid:202) testy dla metody w pó(cid:283)niejszym czasie? Musia(cid:239)by(cid:258) uruchamia(cid:202) zestaw testu o odpowiedniej porze dnia, (cid:285)eby sprawdzi(cid:202), czy test przechodzi pomy(cid:258)lnie? Czy mu- sia(cid:239)by(cid:258) zmienia(cid:202) testy w zale(cid:285)no(cid:258)ci od pory dnia, o jakiej s(cid:200) uruchamiane? B(cid:239)(cid:218)dne wyniki pozytywne i b(cid:239)(cid:218)dne wyniki negatywne Gdyby(cid:258)my pozostawili kod zwracaj(cid:200)cy wiadomo(cid:258)(cid:202) w takim stanie, w jakim jest, i napisali test dla metody, móg(cid:239)by wygl(cid:200)da(cid:202) tak: [Fact] public void GivenEvening_ThenAfternoonMessage() { // Arrange // Act var message = GetGreeting(); // Assert Assert.Equal( Dobry wieczór , message); } Czy dostrzegasz problem? W samym kodzie testu nie ma (cid:285)adnego b(cid:239)(cid:218)du. Problem polega na tym, (cid:285)e kod produkcyjny zwróci inn(cid:200) wiadomo(cid:258)(cid:202) w zale(cid:285)no(cid:258)ci od godziny, w której zostanie uruchomiony. To oznacza, (cid:285)e je(cid:285)eli uruchomisz test do godziny 18, sko(cid:241)czy si(cid:218) powodzeniem. Je(cid:285)eli uruchomisz go pó(cid:283)niej, sko(cid:241)czy si(cid:218) niepowodzeniem. Wyodr(cid:218)bnianie klasy DateTime Klasa DateTime jest cz(cid:218)(cid:258)ci(cid:200) frameworka .NET, a co za tym idzie, powinna zosta(cid:202) wyodr(cid:218)b- niona z naszego systemu. Zazwyczaj chcemy, aby system by(cid:239) zale(cid:285)ny od interfejsów, pozwa- laj(cid:200)c nam podmienia(cid:202) implementacje w czasie wykonywania. 84 Kup książkęPoleć książkę Rozdzia(cid:225) 4. • Co nale(cid:298)y wiedzie(cid:252) przed rozpocz(cid:266)ciem pracy? Oto przyk(cid:239)ad interfejsu ITimeManager: public interface ITimeManager { DateTime Now { get; } } Na potrzeby testów mo(cid:285)esz zbudowa(cid:202) tak(cid:200) implementacj(cid:218) tego interfejsu: public class TestTimeManager : ITimeManager { public Func DateTime CurrentTime = () = DateTime.Now; public void SetDateTime(DateTime now) { CurrentTime = () = now; } public DateTime Now = CurrentTime(); } To pozwala nam ustawi(cid:202) warto(cid:258)(cid:202) aktualnego czasu, dzi(cid:218)ki czemu b(cid:218)dziemy mogli przekaza(cid:202) znan(cid:200) warto(cid:258)(cid:202) do metod testowych. Spójrzmy jeszcze raz na testy: [Theory] [InlineData(18)] [InlineData(19)] [InlineData(20)] [InlineData(21)] [InlineData(22)] [InlineData(23)] public void GivenAfternoon_ThenAfternoonMessage(int hour) { // Arrange var afternoonTime = new TestTimeManager(); afternoonTime.SetDateTime(new DateTime(2017, 7, 13, hour, 0, 0)); var messageUtility = new MessageUtility(afternoonTime); // Act var message = messageUtility.GetGreeting(); // Assert Assert.Equal( Dobry wieczór , message); } [Theory] [InlineData(0)] [InlineData(1)] [InlineData(2)] [InlineData(3)] [InlineData(4)] [InlineData(5)] [InlineData(6)] [InlineData(7)] [InlineData(8)] [InlineData(9)] 85 Kup książkęPoleć książkę TDD w wykorzystaniem C# 7 [InlineData(10)] [InlineData(11)] [InlineData(12)] [InlineData(13)] [InlineData(14)] [InlineData(15)] [InlineData(16)] [InlineData(17)] public void GivenMorning_ThenMorningMessage(int hour) { // Arrange var morningTime = new TestTimeManager(); morningTime.SetDateTime(new DateTime(2017, 7, 13, hour, 0, 0)); var messageUtility = new MessageUtility(morningTime); // Act var message = messageUtility.GetGreeting(); // Assert Assert.Equal( Dzie(cid:241) dobry , message); } Nasz kod produkcyjny wygl(cid:200)da(cid:239)by tak: public class MessageUtility { private readonly ITimeManager _timeManager; public MessageUtility(ITimeManager timeManager) { _timeManager = timeManager; } public string GetMessage() { if (_timeManager.Now.Hour 18) return Dzie(cid:241) dobry ; return Dobry wieczór ; } } Rodzaje sobowtórów testowych Jest wiele rodzajów sobowtórów. Rodzaje te mo(cid:285)na ogólnie pogrupowa(cid:202) jako atrapy, za(cid:258)lep- ki, szpiegi, imitacje i fa(cid:239)szywki. W dalszej cz(cid:218)(cid:258)ci omówimy poszczególne typy i dla ka(cid:285)dego z nich poka(cid:285)emy przyk(cid:239)ady w C# i JavaScripcie. Atrapy Atrapa (ang. dummy) to najprostsza forma sobowtórów testowych. Atrapa nie ma (cid:285)adnej zna- cz(cid:200)cej funkcjonalno(cid:258)ci. Nie oczekujemy, (cid:285)e klasa lub metoda atrapy zostanie wykorzystana do wyprodukowania wyniku w metodzie testowanej. 86 Kup książkęPoleć książkę Rozdzia(cid:225) 4. • Co nale(cid:298)y wiedzie(cid:252) przed rozpocz(cid:266)ciem pracy? Atrapy s(cid:200) stosowane najcz(cid:218)(cid:258)ciej, kiedy testowana klasa ma zale(cid:285)no(cid:258)ci, z których tak naprawd(cid:218) nie korzysta. Atrapy powstaj(cid:200) poprzez stworzenie kopii lub instancji klasy lub metody, nie robi(cid:200)c absolutnie nic w jej ciele. Metody nie zwracaj(cid:200)ce warto(cid:258)ci b(cid:218)d(cid:200) puste, a metody zwracaj(cid:200)ce warto(cid:258)(cid:202) b(cid:218)d(cid:200) zwraca(cid:239)y wyj(cid:200)tek lub najprostsz(cid:200) form(cid:218) typu zwracanego. Atrapa logowania Us(cid:239)uga logowania to doskona(cid:239)y przyk(cid:239)ad elementu do zast(cid:200)pienia atrap(cid:200). Ma(cid:239)o prawdopodobne jest (i nie jest polecane), (cid:285)e podczas testowania metody zechcesz równie(cid:285) testowa(cid:202) logowanie. Przyk(cid:239)ad w C# Oto przyk(cid:239)ad klasy DummyLogger w C#. Zauwa(cid:285), (cid:285)e kiedy wywo(cid:239)ywana jest metoda Log, nic si(cid:218) nie dzieje: enum LogLevel { None = 0, Error = 1, Warning = 2, Success = 3, Info = 4 } interface ILogger { void Log(LogLevel type, string message); } class DummyLogger: ILogger { public void Log(LogLevel type, string message) { // Nie rób nic } } Przyk(cid:239)ad w JavaScripcie Oto przyk(cid:239)ad klasy DummyLogger w JavaScripcie. Zauwa(cid:285), (cid:285)e kiedy wywo(cid:239)ywana jest metoda info, warn, error lub success, nic si(cid:218) nie dzieje: export class DummyLogger { info(message) { } warn(message) { } error(message) { 87 Kup książkęPoleć książkę TDD w wykorzystaniem C# 7 } success(message) { } } Za(cid:258)lepki Za(cid:258)lepka (ang. stub) to kolejny poziom po atrapach. Za(cid:258)lepka daje zawsze t(cid:218) sam(cid:200) odpo- wied(cid:283), niezale(cid:285)nie od przekazanych do niej parametrów. Za(cid:258)lepki s(cid:200) wykorzystywane, kiedy chcesz przetestowa(cid:202) ró(cid:285)ne (cid:258)cie(cid:285)ki wykonania kodu. Przyk(cid:239)adem mo(cid:285)e by(cid:202) b(cid:239)(cid:200)d, który musi zosta(cid:202) zwrócony w przypadku spe(cid:239)nienia konkret- nych warunków. Za(cid:258)lepki powstaj(cid:200) poprzez stworzenie kopii lub nadpisanie klasy b(cid:200)d(cid:283) metody, która ma zwraca(cid:202) warto(cid:258)(cid:202) za(cid:258)lepion(cid:200), i ustawienie jej tak, aby t(cid:218) warto(cid:258)(cid:202) zwraca(cid:239)a. Pami(cid:218)taj: za(cid:258)lepki nie przetwarzaj(cid:200) parametrów, musisz wi(cid:218)c tylko zwróci(cid:202) potrzebn(cid:200) warto(cid:258)(cid:202). Przyk(cid:239)ad w C# Oto przyk(cid:239)ad klasy StubSpeakerContactServiceError w C#. Zauwa(cid:285), (cid:285)e po wywo(cid:239)aniu MessageSpeaker zwracany jest nowy wyj(cid:200)tek, UnableToContactSpeakerException: class StubSpeakerContactServiceError : ISpeakerContactService { public void MessageSpeaker(string message) { throw new UnableToContactSpeakerException(); } } Przyk(cid:239)ad w JavaScripcie Oto przyk(cid:239)ad funkcji stubSpeakerReducer w JavaScripcie. Zauwa(cid:285), (cid:285)e niezale(cid:285)nie od akcji przekazywanej do funkcji do tablicy b(cid:239)(cid:218)dów w stanie aplikacji wstawiany jest b(cid:239)(cid:200)d UNABLE_TO_RETRIEVE_SPEAKERS: import { SpeakerErrors } from ./errors ; import { SpeakerFilters } from ./actions ; const initialState = { speakerFilter: SpeakerActions.SHOW_ALL, speakers: [], errors: [] }; export function stubSpeakerReducer(state, action) { state = state || initialState; state.speakerFilter = action.filter || SpeakerFilters.SHOW_ALL; state.errors.push(SpeakerErrors.UNABLE_TO_RETRIEVE_SPEAKERS); return state; } 88 Kup książkęPoleć książkę Rozdzia(cid:225) 4. • Co nale(cid:298)y wiedzie(cid:252) przed rozpocz(cid:266)ciem pracy? Szpiegi Szpieg (ang. spy) to kolejna ewolucja sobowtórów. Szpieg zwraca warto(cid:258)(cid:202) podobn(cid:200) do za- (cid:258)lepki, ale z jedn(cid:200) bardzo wa(cid:285)n(cid:200) i pomocn(cid:200) ró(cid:285)nic(cid:200): mo(cid:285)e zwraca(cid:202) informacje powi(cid:200)zane z wywo(cid:239)aniem funkcji. Szpiegów najcz(cid:218)(cid:258)ciej si(cid:218) u(cid:285)ywa, kiedy chcesz zweryfikowa(cid:202), czy funkcja zosta(cid:239)a wywo(cid:239)ana z odpowiednimi parametrami. Jest to najbardziej u(cid:285)yteczne podczas testowania granic kodu zewn(cid:218)trznego. Na przyk(cid:239)ad istotne jest, czy aplikacja poprawnie konfiguruje po(cid:239)(cid:200)czenie z baz(cid:200) danych, korzystaj(cid:200)c z danych dost(cid:218)powych pochodz(cid:200)cych z us(cid:239)ugi konfiguracji. Ponadto w niektórych przypadkach trudno zmierzy(cid:202) efekty uboczne wynikaj(cid:200)ce z u(cid:285)ycia testowanej funkcji czy metody. W takich przypadkach mo(cid:285)esz u(cid:285)y(cid:202) szpiega, aby upewni(cid:202) si(cid:218), (cid:285)e ta funkcja czy metoda faktycznie jest wywo(cid:239)ywana. Tworzenia szpiega rozpoczyna si(cid:218) od za(cid:258)lepki, dodaj(cid:200)c funkcjonalno(cid:258)(cid:202) pozwalaj(cid:200)c(cid:200) okre(cid:258)li(cid:202), czy funkcja zosta(cid:239)a wywo(cid:239)ana lub ile razy zosta(cid:239)a wywo(cid:239)ana, albo zwracaj(cid:200)c(cid:200) przekazane do funkcji parametry. Przyk(cid:239)ad w C# Oto przyk(cid:239)ad klasy SpySpeakerContactService, która pozwala okre(cid:258)li(cid:202), czy i ile razy us(cid:239)uga zosta(cid:239)a u(cid:285)yta: class SpySpeakerContactService : ISpeakerContactService { public bool MessageSpeakerHasBeenCalled { get; private set; } public int MessageSpeakerCallCount { get; private set; } public void MessageSpeaker(string message) { MessageSpeakerHasBeenCalled = true; MessageSpeakerCallCount++; } } Przyk(cid:239)ad w JavaScripcie Oto przyk(cid:239)ad funkcji spySpeakerReducer w JavaScripcie. Funkcja ta pozwala okre(cid:258)li(cid:202), ile razy zosta(cid:239)a wywo(cid:239)ana: import { speakerReducer as original_speakerReducer } from ./reducers ; export let callCounter = 0; export function spySpeakerReducer(state, action) { callCounter++; return original_speakerReducer(state,action); } 89 Kup książkęPoleć książkę TDD w wykorzystaniem C# 7 Imitacje Imitacje (ang. mocks) to w zasadzie programowalne szpiegi. Imitacje s(cid:200) u(cid:285)yteczne, kiedy chcesz wykorzysta(cid:202) tego samego sobowtóra testowego w wielu testach. Zwracaj(cid:200) tak(cid:200) war- to(cid:258)(cid:202), jak(cid:200) ustawisz. Nale(cid:285)y jednak pami(cid:218)ta(cid:202), (cid:285)e imitacje wci(cid:200)(cid:285) nie maj(cid:200) w sobie (cid:285)adnej logiki. Zwracaj(cid:200) warto(cid:258)(cid:202), która zosta(cid:239)a wyspecyfikowana, i nie sprawdzaj(cid:200) przekazywanych do funkcji parametrów. Imitacje s(cid:200) u(cid:285)ywane we wszystkich sytuacjach, w których wykorzystywane s(cid:200) atrapy, za(cid:258)lep- ki i szpiegi. Imitacje s(cid:200) bardziej rozbudowanymi implementacjami sobowtórów testowych i z tego powodu nie wsz(cid:218)dzie ich wykorzystanie mo(cid:285)e by(cid:202) po(cid:285)(cid:200)dane. Imitacje s(cid:200) rzadziej u(cid:285)ywane w wielu miejscach, poniewa(cid:285) dla ka(cid:285)dego testu konieczne jest przygotowanie da- nych imitacji, podczas gdy atrapa, za(cid:258)lepka czy szpieg maj(cid:200) ustalone warto(cid:258)ci zwracane i nie musz(cid:200) by(cid:202) konfigurowane. Przygotowanie zwracanych danych testowych jest cz(cid:218)sto trud- niejsze ni(cid:285) stworzenie ca(cid:239)ej za(cid:258)lepki lub szpiega. Imitacje powstaj(cid:200) poprzez skopiowanie klasy lub metody i stworzenie w(cid:239)a(cid:258)ciwo(cid:258)ci, która mo(cid:285)e by(cid:202) ustawiona jako warto(cid:258)(cid:202) zwracana metody. Nast(cid:218)pnie warto(cid:258)(cid:202) ta jest zwracana w meto- dzie imituj(cid:200)cej. Po utworzeniu, przed ka(cid:285)dym testem, nale(cid:285)y ustawi(cid:202) warto(cid:258)(cid:202) imitacji. Przyk(cid:239)ad w C# Oto przyk(cid:239)ad klasy MockDateTimeService w C#. Klasa ta pozwala ustawi(cid:202) dat(cid:218) zwracan(cid:200) przez klas(cid:218), co pozwoli w sposób niezawodny testowa(cid:202), jak inne cz(cid:218)(cid:258)ci systemu b(cid:218)d(cid:200) zachowywa(cid:239)y si(cid:218) w kontek(cid:258)cie okre(cid:258)lonych dat: class MockDateTimeService { public DateTime CurrentDateTime { get; set; } = new DateTime(); public DateTime UTCNow() { return CurrentDateTime.ToUniversalTime(); } } Przyk(cid:239)ad w JavaScripcie Oto przyk(cid:239)ad klasy MockDateTimeService w JavaScripcie. Tak jak w C#, pozwala ona ustawi(cid:202) dat(cid:218) zwracan(cid:200) przez us(cid:239)ug(cid:218), aby móc sprawdzi(cid:202), jak inne cz(cid:218)(cid:258)ci systemu zachowuj(cid:200) si(cid:218) w kontek(cid:258)cie zadanej daty: export class MockDateTimeService { constructor() { this.currentDateTime = new Date(2000, 0, 1); } now() { return this.currentDateTime; } } 90 Kup książkęPoleć książkę Rozdzia(cid:225) 4. • Co nale(cid:298)y wiedzie(cid:252) przed rozpocz(cid:266)ciem pracy? Fa(cid:239)szywki Fa(cid:239)szywka (ang. fake) to ostatni i najbardziej rozbudowany rodzaj sobowtórów testowych. Fa(cid:239)szywka to klasa próbuj(cid:200)ca zachowywa(cid:202) si(cid:218) tak, jakby nie by(cid:239)a sobowtórem. Chocia(cid:285) fa(cid:239)- szywka nie b(cid:218)dzie (cid:239)(cid:200)czy(cid:239)a si(cid:218) z baz(cid:200) danych, b(cid:218)dzie próbowa(cid:239)a zachowywa(cid:202) si(cid:218) tak, jakby faktycznie by(cid:239)a z baz(cid:200) po(cid:239)(cid:200)czona. Fa(cid:239)szywka nie b(cid:218)dzie korzysta(cid:239)a z zegara systemowego, ale b(cid:218)dzie stara(cid:239)a si(cid:218) jak najlepiej odwzorowa(cid:202) korzystanie z niego. Fa(cid:239)szywki albo dodaj(cid:200) dodatkowe funkcje na potrzeby testowania, albo zapobiegaj(cid:200) udzia(cid:239)o- wi zewn(cid:218)trznych bibliotek i systemów w testach. Wi(cid:218)kszo(cid:258)(cid:202) aplikacji jest po(cid:239)(cid:200)czonych z ja- kim(cid:258) (cid:283)ród(cid:239)em danych. Mo(cid:285)e zosta(cid:202) stworzone fa(cid:239)szywe repozytorium korzystaj(cid:200)ce ze swoje- go w(cid:239)asnego (cid:283)ród(cid:239)a danych w pami(cid:218)ci, ale poza tym zachowuj(cid:200)ce si(cid:218) zupe(cid:239)nie jak normalne po(cid:239)(cid:200)czenie z baz(cid:200) danych. Fa(cid:239)szywki s(cid:200) tworzone poprzez wygenerowanie nowej klasy lub metody i zawarcie w niej wy- starczaj(cid:200)cej ilo(cid:258)ci logiki, aby by(cid:239)a nieodró(cid:285)nialna od produkcyjnej klasy lub metody. Jedy- nym istotnym czynnikiem odró(cid:285)niaj(cid:200)cym j(cid:200) od wersji produkcyjnej jest to, (cid:285)e fa(cid:239)szywka nie wykonuje po(cid:239)(cid:200)cze(cid:241) na zewn(cid:200)trz aplikacji i najprawdopodobniej daje testerowi kontrol(cid:218) nad zestawem danych. Przyk(cid:239)ad w C# Oto przyk(cid:239)ad klasy FakeRepository i zwi(cid:200)zanych z ni(cid:200) interfejsów. Jest to fa(cid:239)szywa imple- mentacja generycznego repozytorium: public interface IRepository T { T Get(Func T, bool predicate); IQueryable T GetAll(); T Save(T item); IRepository T Include(Expression Func T, object path); } public interface IIdentity { int Id {get;set;} } public class FakeRepository T : IRepository T where T : IIdentity { private int _identityCounter = 0; public IList T DataSet { get; set; } = new List T (); public T Get(Func T, bool predicate) { return GetAll().Where(predicate).FirstOrDefault(); } public IQueryable T GetAll() { return DataSet.AsQueryable(); 91 Kup książkęPoleć książkę TDD w wykorzystaniem C# 7 } public T Save(T item) { return item.Id == default(int) ? Create(item) : Update(item); } public IRepository T Include(Expression Func T, object path) { // Tutaj nie ma nic do zrobienia, poniewa(cid:285) jest to funkcja na potrzeby EntityFramework // U(cid:285)ywamy Linq to Objects, nie ma wi(cid:218)c potrzeby wykorzystania tej funkcji return this; } private T Create(T item) { item.Id = ++_identityCounter; DataSet.Add(item); return item; } private T Update(T item) { var found = Get(x = x.Id == item.Id); if(found == null) { throw new KeyNotFoundException($ Element o Id {item.Id} nie zosta(cid:239) (cid:180)znaleziony! ); } DataSet.Remove(found); DataSet.Add(item); return item; } } Przyk(cid:239)ad w JavaScripcie Oto przyk(cid:239)ad klasy FakeDataContext w JavaScripcie: export class FakeDataContext { _identityCounter = 1; _dataSet = []; get DataSet() { return this._dataSet; } set DataSet(value) { this._dataSet = value; } get(predicate) { 92 Kup książkęPoleć książkę Rozdzia(cid:225) 4. • Co nale(cid:298)y wiedzie(cid:252) przed rozpocz(cid:266)ciem pracy? if (typeof(predicate) !== function ) { throw new Error( Predykat musi by(cid:202) funkcj(cid:200) ); } const resultSet = this_dataSet.filter(predicate); return resultSet.length = 1 ? {...resultSet[0]} : null; } getAll() { return this._dataSet.map((x) = { return {...x}; }); } save(item) { return item.id ? this.update(item) : this.create(item); } update(item) { if (!this._dataSet.some(x = x.id === item.id)) { this._dataSet.push({...item}); } else { let itemIndex = this._dataSet.findIndex(x = x.id === item.id); this._dataSet[itemIndex] = {...item}; } return {...item}; } create(item) { let newItem = {...item}; newItem.id = this._identityCounter++; this._dataSet.push({...newItem}); return {...newItem}; } } Przyk(cid:239)ad wielopoziomowy Wró(cid:202)my teraz do kontrolera API z rozdzia(cid:239)u 2., „Przygotowanie (cid:258)rodowiska testowego w .NET”. Wpisane w kodzie dane zwracane bezpo(cid:258)rednio przez kontroler nie stanowi(cid:200) dobrej podstawy dla naszej aplikacji. Wi(cid:218)kszo(cid:258)(cid:202) nowoczesnych aplikacji .NET, niezale(cid:285)nie od ich rozmiaru, budowana jest w jakim(cid:258) wariancie architektury wielopoziomowej. Nale(cid:285)y odseparowywa(cid:202) logik(cid:218) biznesow(cid:200) od prezentacji — w tym przypadku prezentacji poprzez ko(cid:241)cówk(cid:218) API. Przygotujemy interfejs dla us(cid:239)ugi mówcy, co pozwoli nam wykorzysta(cid:202) wstrzykiwanie zale(cid:285)- no(cid:258)ci i zapewnienie kontrolerowi konkretnej implementacji us(cid:239)ugi. W kolejnym kroku zwe- ryfikujemy, czy wywo(cid:239)ywana jest odpowiednia metoda nowej us(cid:239)ugi. Aby usun(cid:200)(cid:202) z kontrolera logik(cid:218) biznesow(cid:200), trzeba przebudowa(cid:202) cz(cid:218)(cid:258)(cid:202) testów. 93 Kup książkęPoleć książkę TDD w wykorzystaniem C# 7 Warstwa prezentacji Na pocz(cid:200)tek dodajmy nowy test, sprawdzaj(cid:200)cy, czy kontroler przyjmuje interfejs ISpeakerService: [Fact] public void ItAcceptsInterface() { // Arrange ISpeakerService testSpeakerService = new TestSpeakerService(); // Act var controller = new SpeakerController(testSpeakerService); // Assert Assert.NotNull(controller); } Czas teraz sprawi(cid:202), aby test ko(cid:241)czy(cid:239) si(cid:218) powodzeniem. SpeakerController musi przyjmowa(cid:202) interfejs ISpeakerService, musimy wi(cid:218)c doda(cid:202) pole i konstruktor w klasie kontrolera: public SpeakerController(ISpeakerService speakerService) { } Projekt nie powinien si(cid:218) teraz kompilowa(cid:202). Jest tak dlatego, (cid:285)e w poprzednim przyk(cid:239)adzie z rozdzia(cid:239)u 2., „Przygotowanie (cid:258)rodowiska testowego w .NET”, definiujemy instancj(cid:218) kon- trolera w konstruktorze klasy testowej. Zmodyfikuj konstruktor i dodaj tworzenie instancji TestSpeakerService, która implementuje interfejs ISpeakerService, a nast(cid:218)pnie przeka(cid:285) j(cid:200) do SpeakerController. Klas(cid:218) TestSpeakerService mo(cid:285)esz zdefiniowa(cid:202) wewn(cid:200)trz klasy testów. public SpeakerControllerSearchTests() { var testSpeakerService = new TestSpeakerService(); _controller = new SpeakerController(testSpeakerService); } Teraz nale(cid:285)y si(cid:218) upewni(cid:202), czy metoda Serach klasy SpeakerService jest wywo(cid:239)ywana we- wn(cid:200)trz kontrolera. Ale jak to zrobi(cid:202)? Jednym ze sposobów jest wykorzystanie frameworka imituj(cid:200)cego o nazwie Moq. Moq Aby doda(cid:202) Moq do projektu testowego, kliknij prawym przyciskiem na projekcie testowym i wybierz Zarz(cid:200)dzaj pakietami NuGet…. Odnajd(cid:283) Moq i wybierz instalacj(cid:218) najnowszej sta- bilnej wersji. Nie b(cid:218)dziemy si(cid:218) zbytnio zag(cid:239)(cid:218)bia(cid:202) w tematyk(cid:218) Moqa, ale poka(cid:285)emy, w jaki sposób frameworki imituj(cid:200)ce pomagaj(cid:200) w przygotowaniu testów mo(cid:285)liwo(cid:258)ci aplikacji. Dodaj test sprawdzaj(cid:200)cy, czy metoda Search klasy SpeakerService zosta(cid:239)a wywo(cid:239)ana raz we- wn(cid:200)trz akcji Search kontrolera: 94 Kup książkęPoleć książkę Rozdzia(cid:225) 4. • Co nale(cid:298)y wiedzie(cid:252) przed rozpocz(cid:266)ciem pracy? [Fact] public void ItCallsSearchServiceOnce() { // Arrange // Act _controller.Search( jan ); // Assert _speakerServiceMock.Verify(mock = mock.Search(It.IsAny string ()), Times.Once()); } Aby test zacz(cid:200)(cid:239) przechodzi(cid:202) pomy(cid:258)lnie, musisz wykona(cid:202) jeszcze troch(cid:218) konfiguracji w kon- struktorze klasy testowej: private readonly SpeakerController _controller; private static Mock ISpeakerService _speakerServiceMock; public SpeakerControllerSearchTests() { var speaker = new Speaker { Name = test }; // Zdefiniowanie imitacji _speakerServiceMock = new Mock ISpeakerService (); // Kiedy search zostanie wywo(cid:239)ane, zwró(cid:202) list(cid:218) mówców zawieraj(cid:200)c(cid:200) mówc(cid:218) _speakerServiceMock.Setup(x = x.Search(It.IsAny string ())) .Returns(() = new List Speaker { speaker }); // Przeka(cid:285) obiekt jako ISpeakerService _controller = new SpeakerController(_speakerServiceMock.Object); } Koniecznie zmodyfikuj interfejs, aby aplikacja kompilowa(cid:239)a si(cid:218) poprawnie: public interface ISpeakerService { IEnumerable Speaker Search(string searchString); } Teraz spraw, aby testy przechodzi(cid:239)y, wywo(cid:239)uj(cid:200)c metod(cid:218) Search klasy SpeakerService w me- todzie Search kontrolera. Je(cid:285)eli jeszcze tego nie zrobi(cid:239)e(cid:258), stwórz pole _speakerService, do którego w konstruktorze jest przypisywany parametr speakerService: private readonly ISpeakerService _speakerService; public SpeakerController(ISpeakerService speakerService) { _speakerService = speakerService; } [Route( search )] public IActionResult Search(string searchString) 95 Kup książkęPoleć książkę TDD w wykorzystaniem C# 7 { var hardCodedSpeakers = new List Speaker { new Speaker{Name = Jan }, new Speaker{Name = Janusz }, new Speaker{Name = Janina }, new Speaker{Name = Bartosz }, }; _speakerService.Search( foo ); var speakers = hardCodedSpeakers.Where(x = x.Name.StartsWith(searchString, StringComparison.OrdinalIgnoreCase)).ToList(); return Ok(speakers); } Nast(cid:218)pnie dodajmy test sprawdzaj(cid:200)cy, czy searchString przekazany do metody Search kon- trolera to searchString przekazywany do metody Search klasy SpeakerService: [Fact] public void GivenSearchStringThenSpeakerServiceSearchCalledWithString() { // Arrange var searchString = jan ; // Act _controller.Search(searchString); // Assert _speakerServiceMock.Verify(mock = mock.Search(searchString), Times.Once()); } Aby test przechodzi(cid:239), przeka(cid:285) searchString do metody Search klasy obiektu z pola _speakerService: _speakerService.Search(searchString); Teraz musimy zapewni(cid:202), aby wyniki metody Search klasy SpeakerService by(cid:239)y poprawnie zwracane z akcji: [Fact] public void GivenSpeakerServiceThenResultsReturned() { // Arrange var searchString = jan ; // Act var result = _controller.Search(searchString) as OkObjectResult; // Assert Assert.NotNull(result); var speakers = ((IEnumerable Speaker )result.Value).ToList(); Assert.Equal(_speakers, speakers); } 96 Kup książkęPoleć książkę Rozdzia(cid:225) 4. • Co nale(cid:298)y wiedzie(cid:252) przed rozpocz(cid:266)ciem pracy? Pami(cid:218)taj, (cid:285)e wyniki zwracane przez metod(cid:218) Search klasy SpeakerService s(cid:200) definiowane przez imitacj(cid:218). Musisz wyekstrahowa(cid:202) pole, aby móc przetestowa(cid:202), czy rezultaty zwracane przez akcje s(cid:200) takie same jak te zdefiniowane przez imitacj(cid:218): private readonly SpeakerController _controller; private static Mock ISpeakerService _speakerServiceMock; private readonly List Speaker _speakers; public SpeakerControllerSearchTests() { _speakers = new List Speaker { new Speaker { Name = test } }; _speakerServiceMock = new Mock ISpeakerService (); _speakerServiceMock.Setup(x = x.Search(It.IsAny string ())) .Returns(() = _speakers); _controller = new SpeakerController(_speakerServiceMock.Object); } Wci(cid:200)(cid:285) mamy problem z danymi wpisanymi w kodzie. Nie zapomnij usun(cid:200)(cid:202) niepotrzebnego kodu podczas pracy nad testami. Pami(cid:218)taj: czerwony, zielony, refaktoryzacja. Dotyczy to w takim samym stopniu testów co kodu produkcyjnego. Po usuni(cid:218)ciu danych z kodu mo(cid:285)esz napotka(cid:202) testy ko(cid:241)cz(cid:200)ce si(cid:218) wynikiem negatywnym. Na razie pomi(cid:241) te testy, poniewa(cid:285) t(cid:218) logik(cid:218) b(cid:218)dziemy przenosi(cid:202) do innej cz(cid:218)(cid:258)ci aplikacji. Czas teraz stworzy(cid:202) us(cid:239)ug(cid:218) SpeakerService: xUnit [Fact(Skip= Powód pomini(cid:218)cia )] MSTest [Skip] Warstwa biznesowa Zastanówmy si(cid:218) nad efektywnym organizowaniem testów. Nawigowanie po rozwi(cid:200)zaniu mo(cid:285)e stawa(cid:202) si(cid:218) coraz trudniejsze wraz z rozrostem aplikacji i liczby plików z testami. Jednym z roz- wi(cid:200)za(cid:241) jest tworzenie osobnych folderów dla ka(cid:285)dej testowanej klasy i osobnych plików dla ka(cid:285)dej publicznej metody w ramach danej klasy. Mog(cid:239)oby to wygl(cid:200)da(cid:202) tak: SpeakerService - Search Nie musisz zabiera(cid:202) si(cid:218) za to teraz, ale nie zaszkodzi mie(cid:202) planu na przysz(cid:239)o(cid:258)(cid:202). Aplikacje rozrastaj(cid:200) si(cid:218) do(cid:258)(cid:202) szybko i zanim si(cid:218) obejrzysz, b(cid:218)dziesz mie(cid:202) w swoim rozwi(cid:200)zaniu trzyna- (cid:258)cie projektów. Na t(cid:218) chwil(cid:218) mo(cid:285)esz zdecydowa(cid:202) si(cid:218) stworzy(cid:202) projekt Services z folderem ServicesTest, aby oddzieli(cid:202) warstw(cid:218) biznesow(cid:200) i zwi(cid:200)zane z ni(cid:200) testy od warstwy prezentacji i testów z ni(cid:200) zwi(cid:200)zanych. Zostawimy to jako (cid:202)wiczenie dla Czytelnika. Stwórzmy teraz klas(cid:218) SpeakerService — tutaj b(cid:218)dziesz tworzy(cid:202) wszystkie metody testowe dla metody Search klasy SpeakerService: 97 Kup książkęPoleć książkę TDD w wykorzystaniem C# 7 [Fact] public void ItExists() { var speakerService = new SpeakerService(); } Kiedy sprawisz, (cid:285)e test zacznie przechodzi(cid:202), stwórz kilka testów, które potwierdz(cid:200), i(cid:285) metoda Search istnieje i zwraca kolekcj(cid:218) mówców: [Fact] public void ItHasSearchMethod() { var speakerService = new SpeakerService(); speakerService.Search( test ); } Nast(cid:218)pnie sprawd(cid:283), czy SpeakerService implementuje interfejs ISpeakerService: [Fact] public void ItImplementsISpeakerService() { var speakerService = new SpeakerService(); Assert.True(speakerService is ISpeakerService); } Klasa SpeakerService powinna teraz wygl(cid:200)da(cid:202) podobnie jak tu: public class SpeakerService : ISpeakerService { public IEnumerable Speaker Search(string searchString) { return new List Speaker (); } } Pami(cid:218)taj: posuwaj si(cid:218) do przodu powoli i metodycznie.
Pobierz darmowy fragment (pdf)

Gdzie kupić całą publikację:

TDD z wykorzystaniem C# 7. Programowanie sterowane testami
Autor:
,

Opinie na temat publikacji:


Inne popularne pozycje z tej kategorii:


Czytaj również:


Prowadzisz stronę lub blog? Wstaw link do fragmentu tej książki i współpracuj z Cyfroteką: