JUNIT Terminologia Test coverage zarkes badań testowany kawałek kodu Test lokalny local test badanie pojedynczej klasy itp. Text fixture armatura pomiarowa (wat?) punkt odniesienia do testów Integration test test integracyjny nazywany czasem testem funkcjonalnym oznacza testowanie zachowania komponentu lub integracji między kompotentami (odzwierciedlają interakcję między użytkownikiem a programem) Performance tests test wydajności w sposób powtarzalny testują oprogramowanie pod kątem wydajności Test jest testem zachowania (również testem integracyjnym) kiedy nie sprawdza wyników tylko czy konkretne metody były wywołane z poprawnymi danymi wejściowymi. State test test stanu testowanie stanu określa walidację wyniku, natomiast testowanie zachowania testuje zachowanie aplikacji testowej. Organizacja testów Testy jednostkowe są tworzone w osobnych projektach lub innych folderach źródłowych aby ominąć pomieszania się kodów. Najczęście używane testowe frameworki: JUnit i TestNG. Użycie JUnit JUnit test to metoda zawarta w klasie która służy jedynie do testów. Nazywana jest klasą testową. Do takiej klasy trzeba dodać adnotację. Przykład: public void multiplicationofzerointegersshouldreturnzero() { // MyClass is tested MyClass tester = new MyClass(); // Tests assertequals("10 x 0 must be 0", 0, tester.multiply(10, 0)); assertequals("0 x 10 must be 0", 0, tester.multiply(0, 10)); assertequals("0 x 0 must be 0", 0, tester.multiply(0, 0)); Nazewnictwo do klas dodajemy Test, do metod should, nazwa metody powinna mówić co testuje
Zestawy testów testu suites klasy testowe mogą być łączone w zestawy. Uruchamianie zestawu wykona wszystkie klasy w tym zestawie w określonej kolejności. package com.vogella.junit.first; import org.junit.runner.runwith; import org.junit.runners.suite; import org.junit.runners.suite.suiteclasses; @RunWith(Suite.class) @SuiteClasses({ MyClassTest.class, MySecondClassTest.class ) public class AllTests { Do regularnego testowania zazwyczaj używa się Apache Ant lub Apache Maven. Klasa org.junit.runner.junitcore dostarcza metodę runclasses()która pozwala uruchomić jeden lub kilka kals testowych. Jako parametr zwrotny otrzymasz obiekt typu org.junit.runner.result. Obiekt ten może zostać użyty do otrzymania informacji o testach. package de.vogella.junit.first; import org.junit.runner.junitcore; import org.junit.runner.result; import org.junit.runner.notification.failure; public class MyTestRunner { public static void main(string[] args) { Result result = JUnitCore.runClasses(MyClassTest.class); for (Failure failure : result.getfailures()) { System.out.println(failure.toString()); Do odtworzenia testów jednostkowych musisz dodać bibliotekę JUnit do ścieżki classpath w programie. Konstrukcja kodu JUnit Tabela adnotacji: Adnotacja Opis public void method() Adnotacja określa metodę jako metodę testową. (expected = Exception.class) Zwraca niepowodzenie, jeśli metoda nie rzuca zadeklarowanego wyjątku (timeout=100) Zwraca niepowodzenie, jeśli metoda trwa więcej niż 100 ms.
Adnotacja Opis @Before public void method() Ta metoda jest wykonywana przed każdym testem. Jest używana do przygotowania środowiska testowego (np. Wczytanie danych z pliku) @After public void method() Ta metoda jest wykonywana po każdym teście. Jest wykorzystywana do oczyszczenia środowiska testowego (np. usuwanie tymczasowych danych, przywracanie ustawień defaultowych ). Pozwala też zaoszczędzić pamięć czyszcząc drogie struktury pamięciowe. @BeforeClass public static void method() Ta metoda jest wykonywana raz przed startem wszystkich testów. Jest stosowana do wykonywania czasochłonnych czynności, na przykład, do łączenia się z bazą danych. Metody oznaczone tą adnotacją muszą być statyczne! @AfterClass public static void method() Analogicznie ta metoda wykonywana jest raz po ukończeniu wszystkich testów. Jest stosowana do wykonywania działań porządkowych, np. przerwanie połączenia z bazą danych. Znów tylko metody statyczne! @Ignore Ignoruje metodę testową. Przydatne, gdy kod bazowy został zmieniony a metoda testowa jeszcze nie jest zaadaptowana do pracy z nim. JUnit dostarcza statycznych metod w klasie Assert do testowania określonych warunków. Zazwyczaj metody te zaczynają się słowem assert i pozwalają określić komunikat o błędzie, oczekiwany i rzeczywisty wynik. Metody te porównują wartość rzeczywistą zwracaną w teście z wartością oczekiwaną. Rzucają AssertionException jeśli test się nie powiedzie. Tabela metod (parametry w [ ] są opcjonalne): Metoda Opis fail([string message]) Failuje test (nie wiem jak to określić >.<). Opcjonalny String określa wiadomość z jaką failuje test. asserttrue([message], boolean condition) Sprawdza czy condition to true. Wiadomość jak wcześniej. assertfalse([message], boolean condition) Sprawdza czy condition to false. Wiadomość jak wcześniej. assertequals([string message], expected, actual) Testuje czy dwie zmienne są takie same. UWAGA! Dla tablic porównuje referencje, a nie zawartości! assertequals([string message], expected, actual, tolerance) Porównuje wartości float i double. Tolerance jest liczbą miejsc po przecinku, które muszą być takie same. assertnull([message], object) Sprawdza czy obiekt to null. assertnotnull([message], object) Sprawdza czy obiekt to nie null. assertsame([string], expected, actual) Sprawdza czy dwie zmienne odnoszą się do tego samego obiektu.
Metoda Opis assertnotsame([string], expected, actual) Sprawdza czy dwie zmienne nie odnoszą się do tego samego obiektu (jakoś to mało po polsku, ale nie jestem humanistą, wiesz o co chodzi ;..;) Kolejność wykonywania złota zasada: TESTY NIE POWINNY BYĆ OD SIEBIE ZALEŻNE, tzn. dobrze napisane testy ^^. W wersji 4.11 JUnit dodano adnotację @FixMethodOrder(MethodSorters.NAME_ASCENDING), która służy do zdefiniowania, że metody testowe są posortowane przez nazwę metody w porządku leksykograficznym. Jako argumenty wywołania tej deklaracji możesz użyć też np. MethodSorters.DEFAULT. To jest domyślne sortowanie używane przez JUnit, jeśli tego nie zrobisz to wyjdzie na to samo, to po prostu w sposób jawny definiuje to sortowanie. Jest też np. MethodSorters.JVM, który używa sortowania domyślnego wirtualnej maszyny Javy które mogą zmieniać się przy kolejnych uruchomieniach. Instalacja JUnit W Eclipse JUnit jest zintegrowana od początku więc nie musisz nic instalować. Jako, że jestem zwolennikiem IntelliJ, to dodam że bibioteke musisz dodać samodzielnie. Strona domowa JUnit: http://junit.org/ Ściągnij stamtąd bibliotekę.jar wersji JUnit która Ci odpowiada oraz.jar Hamcrest (nie zapomnij ściągnąć obydwu!). Następnie utwórz projekt lub otwórz już istniejący. Użyj kombinacji klawiszy Ctrl + Shift + Alt + S, po lewej wybierz Modules z panelu Project Settings, a następnie wciśnij zielony przycisk +, później Library i Java. Wybierz pliki.jar z bibliotekami, zatwierdź i voilà. JUnit w IntelliJ ( ʖ ) Tworzymy test w IntelliJ Oczywiście możesz pisać ręcznie testy, ale po co utrudniać sobie życie? Pokażę Ci parę tricków, dzięki którym sprawnie i przyjemnie utworzysz potrzebne testy w naszym ukochanym IDE. Szybki sposób: Zaznaczamy jakieś miejsce wewnątrz klasy, którą chcemy przetestować (w tym przypadku naszej klasy Odejmowacz ). Używamy kombinację klawiszy CTRL + SHIFT + T. Pojawi się nam takie oto okienko. Klikamy Create New Test
(1) (2) Naszym oczom ukazuje się okno kreatora testów. Zaznaczamy metody które chcemy przetestować (1), a następnie zatwierdzamy (2). Kreator utworzy klasę oraz metody testowe. Oczywiście implementację musisz wykonać sam.
Dokładny sposób: Na pasku wybieramy Run, a następnie Edit Configurations. Naciskamy zielonego plusa w lewym górnym roku nowo otwartego okna i wybieramy JUnit. Otwiera nam się okny w którym mamy dużo opcji pomagających nam utworzyć pożądane testy. Jak widzisz na screenie można utworzyć testy jednej klasy czy metody, ale można też tworzyć grupy testów, dzięki czemu odpalając jeden test możemy odpalić np. wszystkie testy w pakiecie. Można też nadać nazwę naszemu testowi.
Odpalamy test w IntelliJ Znów pokażę Ci dwa sposoby odpalenia testów w IntelliJ. Pierwszy sposób służy do odpalenia pojedynczych klas lub metod testowych. Klikamy prawym przyciskiem myszy na metodzie (lub klasie) testowej i naciskamy Run zaznaczony na screenie, co spowoduje uruchomienie wybranego testu jeśli operujemy na metodzie, lub testów w klasie, jeśli wybraliśmy klasę. Opcja Run with Coverage dodatkowo poinforume nas w danych procentowych ile kodu pokryliśmy. Spróbuj odpalić swój test używając obydwu opcji. Druga opcja pozwala nam odpalić testy utworzone metodą dokładną. Panel ten znajduje się w prawym górnym rogu okna głównego naszego IDE. Jak widzisz opcje uruchomienia mamy podobne jak w poprzednim przypadku, lecz tutaj wybieramy z listy test, który chcemy uruchomić. Ukończony test w IntelliJ (2) Po ukończeniu testu na dole ukażą się nam wyniki (lub błędy). (5) (3) (1) (4)
Okno (1) przedstawia nam ukończone testy oraz prezentuje graficznie ich wyniki. Zaznaczając opcję (2) w (1) widnieć będą tylko testy ukończone niepowodzeniem. W oknie (3) znajdziemy zwrócone wartości oraz przechwycone błędy. Natomiast w ostatnim oknie (4) mamy takie dane jak np. czas ukończenia poszczególnych testów. Więcej opcji możemy wybrać za pomocą rozwijanej listy kryjącej się pod ikoną koła zębatego (5). Zaawansowane opcje JUnit Testy sparametryzowane JUnit pozwala używać parametrów w klasach testowych. Taka klasa może zawierać jedną metodę testową i metoda ta jest wykonywana z róznymi parametrami. Sparametryzowaną klasę testową zaznaczsz adnotacją @RunWith(Parameterized.class). Klasa ta musi zawierać statyczną metodę z adnotacją @Parameters która generuje i zwraca kolekcję tablic (naprawdę ^^). Każda przedmiot w tej kolekcji jest używany jako parametr metody testowej. Ponadto musisz utworzyć konstruktor w którym przechowasz wartości dla każdego testu. Ilość elementów w każdej tablicy uzyskanych metodą oznaczoną @Parameters musi odpowiadać liczbie parametrów w konstruktorze klasy. Klasa jest tworzona dla każdego parametru, a wartości testowe są przekazywane przez konstruktor. package de.vogella.junit.first; import static org.junit.assert.assertequals; import java.util.arrays; import java.util.collection; import org.junit.test; import org.junit.runner.runwith; import org.junit.runners.parameterized; import org.junit.runners.parameterized.parameters; @RunWith(Parameterized.class) public class MyParameterizedClassTest { private int multiplier; public MyParameterizedClassTest(int testparameter) { this.multiplier = testparameter; // creates the test data @Parameters public static Collection<Object[]> data() { Object[][] data = new Object[][] { { 1, { 5, { 121 ; return Arrays.asList(data); public void testmultiplyexception() { MyClass tester = new MyClass(); assertequals("result", multiplier * multiplier, tester.multiply(multiplier, multiplier));
Rules Dzięki adnotacji @Rule możesz stworzyć obiekty które mogą być użyte i skonfigurowane w Twojej metodzie testowej. package de.vogella.junit.first; import org.junit.rule; import org.junit.test; import org.junit.rules.expectedexception; public class RuleExceptionTesterExample { @Rule public ExpectedException exception = ExpectedException.none(); public void throwsillegalargumentexceptionificonisnull() { exception.expect(illegalargumentexception.class); exception.expectmessage("negative value not allowed"); ClassToBeTested t = new ClassToBeTested(); t.methodtobetest(-1); Mechanizm ten dostarcza również kilka użytecznych implementacji. Na przykład, klasa TemporaryFolder pozwala utworzyć pliki i foldery, które po teście automatycznie są usuwane. package de.vogella.junit.first; import static org.junit.assert.asserttrue; import java.io.file; import java.io.ioexception; import org.junit.rule; import org.junit.test; import org.junit.rules.temporaryfolder; public class RuleTester { @Rule public TemporaryFolder folder = new TemporaryFolder(); public void testusingtempfolder() throws IOException { File createdfolder = folder.newfolder("newfolder"); File createdfile = folder.newfile("myfilefile.txt"); asserttrue(createdfile.exists()); Kategorie Możliwe jest również definiowanie kategorii testów i włączanie ich lub wyłączanie na podstawie adnotacji. public interface FastTests { /* category marker */
public interface SlowTests { /* category marker */ public class A { public void a() { fail(); @Category(SlowTests.class) public void b() { @Category({ SlowTests.class, FastTests.class ) public class B { public void c() { @RunWith(Categories.class) @IncludeCategory(SlowTests.class) @SuiteClasses({ A.class, B.class ) // Note that Categories is a kind of Suite public class SlowTestSuite { // Will run A.b and B.c, but not A.a @RunWith(Categories.class) @IncludeCategory(SlowTests.class) @ExcludeCategory(FastTests.class) @SuiteClasses({ A.class, B.class ) // Note that Categories is a kind of Suite public class SlowTestSuite { // Will run A.b, but not A.a or B.c