Testowanie/GRASP 23 października 2018
Testy jednostkowe Testy jednostkowe (unit tests) Test jednostkowy to kod, który ma na celu zapewnić, że inny kod działa poprawnie. Główna idea: najpierw test, potem implementacja, czyli: trochę testowania, trochę implementacji. Stosowane sa podczas tworzenia zgodnie z metodyka Test-driven development. Korzyści: błędy znajdowane sa wcześniej, mniej debuggowania, kod jest bardziej stabilny i czytelny.
Testy jednostkowe >>
Testy jednostkowe Kiedy pisać? W najbardziej skomplikowanych i krytycznych częściach kodu. Nie ma sensu tworzyć testów dla trywialnego kodu. Dobry zestaw testów zapobiega błędom podczas tworzenia nowych funkcjonalności. Gdy kod nie ma pokrycia testami, warto zaczać od takiego kodu, w którym jest duże prawdopodobieństwo wystapienia błędów. Przykład Largest method
Główne klasy Klasy pakietu junit.framework 1 Assert - zawiera metody asercji. 2 TestCase- definiuje elementy do wywoływnia wielu testów (metody: setup, teardown). 3 TestResult - przechowuje rezultaty wykonywania testów. 4 TestSuite - wywołuje wiele klas testowych: @RunWith(Suite.class), @Suite.SuiteClasses.
Klasa Assert Podstawowe metody 1 // Check that two objects are equal 2 assertequals ( str1, str2 ); 3 // Check that a condition is true 4 asserttrue ( val1 < val2 ); 5 // Check that a condition is false 6 assertfalse ( val1 > val2 ); 7 // Check that an object isn 't null 8 assertnotnull ( str1 ); 9 // Check that an object is null 10 assertnull ( str3 ); 11 // Check if two references point to the same object 12 assertsame ( str4, str5 ); 13 // Check if two references not point to the same object 14 assertnotsame ( str1, str3 ); 15 // Check whether two arrays are equal to each other. 16 assertarrayequals ( expectedarray, resultarray ); Przykład UnitTesting->TestAssertion
Klasa TestCase Podstawowe metody 1 public class JUnitTestCase extends TestCase { 2 3 public void setup () { 4 } 5 6 @Test 7 public void testadd () { 8 } 9 10 public void teardown ( ) { 11 } 12 } Przykład UnitTesting->JUnitTestCase
Klasa TestResult Podstawowe metody 1 Result result = JUnitCore. runclasses ( JUnit. class ); 2 for ( Failure failure : result. getfailures ()) { 3 System. out. println ( failure. tostring ()); 4 } 5 System. out. println ( result. wassuccessful ()); Przykład UnitTesting->TestRunner
JUnit Annotations Podstawowe adnotacje 1 public class JUnitAnnotation { 2 3 @BeforeClass 4 public static void beforeclass () { 5 } 6 @AfterClass 7 public static void afterclass () { 8 } 9 @Before 10 public void before () { 11 } 12 @After 13 public void after () { 14 } 15 @Test 16 public void test () { 17 } 18 @Ignore 19 public void ignoretest () { 20 } 21 }
JUnit Annotations Przykład UnitTesting->JUnitAnnotation UnitTesting->JUnitAnnotationExample UnitTesting->JUnitException
Klasa TestSuite Podstawowe metody 1 @RunWith ( Suite. class ) 2 @Suite. SuiteClasses ({ 3 TestAssertions. class, 4 JunitAnnotation. class, 5 TestJunit. class 6 }) 7 8 public class TestSuite { 9 } Przykład UnitTesting->TestSuite
Obiekty imitacji Obiekty imitacji (Moking objects) Mocking to technika testowania charakteryzujaca sie tym, że rzeczywiste obiekty zastapione sa ich imitacja z predefiniowanym zachowaniem. Obiekt imitacyjny to obiekt, który dla określonego wejścia ma zdefiniowane określone wyjście bez podejmowania określonych akcji. Przykład JUnitMockCalculation JUnitWithMockito
Obiekty imitacji Kiedy stosować? Do testowania komponentów, które zależa od komponentów jeszcze nie zaimplementowanych. Gdy rzeczywiste komponenty działaja zbyt powoli. Gdy infrastuktura (a raczej jej brak) uniemożliwia rzeczywiste testowanie. Mockito-tutorial https://examples.javacodegeeks.com/core-java/mockito/mockitotutorial-beginners/
Zasady GRASP Expert Creator Low Coupling High Cohesion Polymorphism Indirection Pure Fabrication Protected Variations Controller
Expert >> Product Description -zna cenę produktu za sztukę SalesLineItem-zna liczbę sztuk danego produku Sale - zna zakupione produkty Która klasa powinna wyliczać całkowita kwotę sprzedaży?
Expert - czyli ten, który wie, co robi Przydziel zobowiazanie ekspertowi: klasie, która ma informacje konieczne do jego realizacji. Gdy informacja jest rozproszona po różnych klasach konieczna może być współpraca ekspertów. Zysk: wspiera hermetyzację i równomierny podział odpowiedzialności, lżejsze i czytelniejsze klasy.
Expert - rozwi zanie >>
Creator >> Kto powinien być odpowiedzialny za utworznie nowej instancji klasy SalesLineItem?
Creator Nowa instancję A powinna tworzyć klasa B, która: Posiada, pamięta lub bezpośrednio używa A Posiada dane inicjalizujace dla A Gdy wiele klas spełnia warunki, wybierz B zawierajace A Zysk: mniejsza liczba powiazań, kod łatwy w utrzymaniu.
Creator - rozwi zanie >>
Sprz»enie (ang. Coupling) Zależności między klasami: atrybuty, argumenty metod, wywołania metod, dziedziczenie... Sprzężenie - miara określajaca w jakim stopniu dana klasa jest zależna od innych klas Problemy z wysokim sprzężeniem klasy A: zmiany w innych klasach wymuszaja zmiany w A trudno jest zrozumieć klasę A w izolacji trudno jest powtórnie użyć klasę A, bo potrzebne sa też obiekty z nia powiazane.
Niskie sprz»enie (Low Coupling) Jak lepiej powiazać instancje klas Payment i Sales? >>
Niskie sprz»enie (Low Coupling) >>
Niskie sprz»enie - zasady Oceniajac różne możliwości przydzielaj metody tak, aby ograniczać sprzężenie między klasami. Zysk: łatwiejsze ponowne wykorzystanie kodu, ograniczenie zasięgu zmian. Pewnien stopień powiazania jest nieunikniony, bo obiekty musza się komunikować.
Spójno± (ang. Cohesion) Spójność - miara określajaca w jakim stopniu metody klasy sa do siebie podobne i ze soba powiazane (tzn. podobna funkcjonalność i stopień ogólności) Niska spójność powoduje, że: trudno zrozumieć kod i cel istnienia klasy trudno jest klasę utrzymywać, rozwijać i ponownie wykorzystywać.
Jak oceni spójno±? Bardzo niska spójność - bardzo dużo zadań w różnych obszarach funkcjonalnych (np. klasa odpowiada za logikę i za interakcję z baza danych) Niska spójność - wiele bardzo różnych zadań w jednym obszarze funkcjonalnym (np. jedna klasa w pełni odpowiada za interakcję z baza danych) Wysoka spójność - mała liczba podobnych zobowiazań w ustalonym obszarze funkcjonalnym (np. grupa klas realizuje obsługę bazy danych)
Spójno± >>
High Cohesion - zasada ewaluacyjna Oceniajac różne możliwości przydzielaj metody tak, aby spójność pozostała wysoka Zysk: łatwy w utrzymaniu kod High Cohesion = Low Coupling
Pure Fabrication - czysty wymysª Problem: ograniczenie się w modelu projektowym do obiektów z modelu dziedziny może prowadzić do niskiej spójności i wysokiego sprzężenia. Pomysł: wymyśl klasę pomocnicza spoza modelu dziedziny i przydziel jej odpowienie zobowiazania. Przykład: gdy każda klasa odpowiada za swój zapis do bazy danych to występuje wysokie sprzężenie i niska spójność. Stwórzmy więc specjalne klasy do obsługi bazy danych.
Indirection - czyli po±rednictwo Unikaj bezpośrednich powiazań do elementów niestabilnych. W razie potrzeby wykorzystaj obiekt pośredniczacy. >>
Polymorphism Polimorfizm - oznacza nadanie tej samej nazwy metodom różnych klas, gdy metody sa ze soba powiazane. overriding - klasy maja wspólny interfejs lub nadklasę, ale maja taka sama sygnaturę. overloading - metody maja taka sama nazwę, ale w zależności od sygnatury moga mieć inny kod.
Polimorfizm - przykªad >> Sale będzie musiała wyliczyć podatek przy użyciu zewnętrznego kalkulatora podatków Kalkulatorów jest dużo - maja różne interfejsy Jaki obiekt powinien odpowiadać za ich obsługę?
Polimorfizm - przykªad >> Zobowiazanie ma postać polimorficznej metody gettaxes Zasada ogólna: użyj polimorfizmu jeśli metoda zależy od typu (zamiast if-then-else)
Protected Variations Jak projektować system by był odporny na zmiany i łatwo rozszerzalny? Zasada ogólna: rozpoznawaj miejsca, w których zmiany moga się pojawić i otaczaj je stabilnym interfejsem. Przykład: kalkulator podatków jest punktem zmienności (prawo się zmienia, wielu usługodawców, różne API)
Protected Variations Open/close principle - klasy powinny być otwarte na rozszerzenia i zamknięte na modyfikacje. Law of Demeter czyli: nie rozmawiaj z obcymi. Programowanie refleksyjne
Protected Variations - przykład Kiepsko - co jeśli trzeba dodać kolejny kształt? 1 public class Rectangle { 2 public double Length ; 3 public double Width ; 4 } 5 public class Circle { 6 public double Radius ; 7 } 8 public double Area ( object [] shapes ) 9 { 10 double area = 0; 11 for ( Object shape in shapes ) 12 { 13 if ( shape instanceof Rectangle ) 14 { 15 Rectangle rectangle = ( Rectangle ) shape ; 16 area += rectangle. Width * rectangle. Height ; 17 } 18 else 19 { 20 Circle circle = ( Circle ) shape ; 21 area += circle. Radius * circle. Radius * Math. PI ; 22 } 23 } 24 return area ; 25 }
Protected Variations - przykład Lepiej - Klas nie trzeba modyfikować, ale można dodać inne 1 public class Rectangle { 2 public double Length ; 3 public double Width ; 4 public double Area () 5 { 6 return Width * Height ; 7 } 8 } 9 10 public class Circle { 11 public double Radius ; 12 public double Area () 13 { 14 return Radius * Radius * Math. PI ; 15 } 16 } 17 18 public double Area ( Shape [] shapes ) 19 { 20 double area = 0; 21 for ( Shape shapeinstance in shapes ) 22 { 23 area += shapeinstance. Area (); 24 } 25 return area ; 26 }
Law of Demeter Klasa może wysłać komunikat do: Obiektu this metody parametru obiektu utworzonego wewnatrz metody Źle 1 AccountHolder holder = 2 sale. getpayment (). getaccount (). getaccountholder (); Dobrze 1 AccountHolder holder = 2 sale. getaccountholderofpayment ();
Programowanie refleksyjne Wybór i wiazanie obiektów w czasie wykonywania progamu Bez refleksji 1 Foo foo = new Foo (); 2 foo. hello (); Z użyciem refleksji 1 Class c = Class. forname (" Foo " ); 2 Method m = c. getmethod (" hello " ); 3 m. invoke (c. newinstance ());
SOLID Single Responsibility Open - close principle Liskov substitution Interface segregation Dependancy inversion
Dependancy inversion - klasyczna architektura warstwowa Uzależniać klasy od abstrakcji a nie od konkretnych klas. Warstwa to grupa pakietów o podobnym zakresie odpowiedzialności. Warstwy układaja się tak, aby wyższe wywoływały usługi niższych, a niższe nie zależały od wyższych. >>
Dependancy inversion - odwrócenie klasycznych zale»no±ci Wyższe warstwy zależa od interfejsów a nie od wartw niższych. >>
Inne wa»ne zasady KISS - Keep it Simple, Stupid YAGNI - You Ain t Gonna Need it DRY - Don t Repeat Yourself.