JavaRush /Blog Java /Random-PL /JUnit dla JavaRush, czyli trochę o testowaniu w domu.
Sdu
Poziom 17

JUnit dla JavaRush, czyli trochę o testowaniu w domu.

Opublikowano w grupie Random-PL
Masz dość wpisywania danych testowych do konsoli dziesiątki razy, aby sprawdzić swoje zadanie? Witamy w kocie, powiem Ci co możesz z nim zrobić. Ostatecznym celem tego materiału będzie automatyzacja uruchamiania rozwiązywanego zadania z różnymi parametrami i sprawdzanie wyników bez wprowadzania zmian w jego kodzie źródłowym. Jak zapewne już zrozumiałeś z tytułu, naszym głównym asystentem w tej dość prostej sprawie będzie JUnit . Jeśli jeszcze nie słyszałeś o testach jednostkowych i testach jednostkowych , proponuję zrobić sobie małą przerwę i zapoznać się z tymi pojęciami, na szczęście informacji w Internecie jest wystarczająco dużo. Nie, nie chcesz? No cóż, myślę, że nie będzie to dużym problemem w zrozumieniu tego, co się dzieje. W końcu wiesz, czym jest test i testowanie w ogóle? Robisz to za każdym razem, gdy uruchamiasz zadanie, wprowadzasz początkowe dane i porównujesz wynik z tym, czego się spodziewałeś.
Witaj, świecie JUnit!
Co to jest JUnit? Na oficjalnej stronie projektu możemy przeczytać następujący opis:
JUnit to prosty framework do pisania powtarzalnych testów. Jest to instancja architektury xUnit dla frameworków do testów jednostkowych.
Dla nas oznacza to możliwość napisania specjalnie zaprojektowanych klas, których metody będą współdziałać z naszym programem, porównać wynikowy wynik z referencyjnym i poinformować nas, jeśli nie pasują. Aby zrozumieć zasadę, rozważmy prosty przykład. Załóżmy, że mamy klasę pomocniczą, której jedna z metod pobiera dwie zmienne typu int i zwraca ich sumę: JUnit dla JavaRush, czyli trochę o testowaniu w domu.  - 1 To jest funkcjonalność, którą spróbujemy przetestować. Na szczęście w naszym ulubionym IDEA jest już wszystko, co potrzebne do szybkiego tworzenia testów, wystarczy, że ustawimy kursor w linii deklaracji klasy, wciśniemy „Alt + Enter” i z menu kontekstowego wybierzemy „Utwórz test”: Po określeniu JUnit dla JavaRush, czyli trochę o testowaniu w domu.  - 2 gdzie powinieneś stworzyć test, IDEA sugeruje wybranie biblioteki testowej (w tym materiale korzystam z JUnit4; aby klasy z biblioteki można było połączyć z projektem należy kliknąć przycisk „Napraw”), metod do przetestowania i dodatkowych opcje. JUnit dla JavaRush, czyli trochę o testowaniu w domu.  - 3 IDE utworzy szablon klasy testowej: ClassName = TestClassName + "Test" MethodName = "test" + TestMethodName JUnit dla JavaRush, czyli trochę o testowaniu w domu.  - 4 Musimy tylko wypełnić treść metody. Pomogą w tym tak zwane „Asercje” , metody dostarczane przez JUnit . W uproszczeniu ich działanie wygląda następująco: oczekiwany wynik oraz wynik wywołania testowanej metody przekazywane są do metody .assert*, dla wygody jako pierwszy parametr można dodać komunikat objaśniający. Jeżeli w trakcie testu parametry nie będą się zgadzać zostaniesz o tym poinformowany. Możesz uruchomić klasę testową do wykonania, tak jak zwykłą klasę, wolę używać kombinacji klawiszy Ctrl+Shift+F10 JUnit dla JavaRush, czyli trochę o testowaniu w domu.  - 5
Określmy zadanie
Teoretycznie wszystko jest proste i piękne, ale w kontekście proponowanego przykładu nie jest to wcale konieczne, możemy zaufać komputerowi, że doda dwie liczby. Bardziej interesuje nas, jak potoczą się sprawy, gdy uczniowie JavaRush rozwiążą rzeczywiste problemy, na przykład sugeruję skorzystanie z ukochanego poziomu 05.lekcja12.bonus03.
/* Problem z algorytmami Napisz program, który: 1. wprowadzi z konsoli liczbę N > 0 2. następnie wprowadzi z konsoli N liczb 3. wyświetli maksimum z N wprowadzonych liczb. */
Musimy napisać trzy testy, dla liczb dodatnich, ujemnych i zbioru mieszanego.
Im dalej w las...
Tutaj czekają na nas niespodzianki: public class UtilApp { public static void main(String[] args) throws Exception { BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); //напишите здесь ваш kod int n; int maximum; /* Конечно же я не буду размещать решение задачи ;) Код приведенный тут переработан для наглядности, и не в коем случае не означает что он должен присутствовать в "правильном решении" */ System.out.println(maximum); } }
  • Logika programu jest umieszczona w metodzie main().
  • Dane źródłowe nie są przekazywane do metody, lecz wprowadzane z klawiatury.
  • Metoda main() nie zwraca wyniku, ale wyświetla go na konsoli.
O ile pierwszy punkt nie jest szczególnie problematyczny (możemy wywołać metodę main() jak zwykle), to dwa kolejne zmuszają nas do głębszego zagłębienia się w temat i wytężenia mózgu. Znalazłem kilka rozwiązań problemu:
  1. Przeniesienie logiki znajdowania maksimum do osobnej metody.
    • Plusy: Prawidłowe podejście w zakresie refaktoryzacji
    • Wady: Program zarasta kodem, niepotrzebnymi strukturami, dodawana jest przynajmniej tablica lub ArrayList (w zależności od gustu i koloru...). Testuje się jedynie mechanizm znajdowania maksimum, nie sprawdza się danych wejściowych i wyjściowych.
  2. Pisanie opakowań dla System.in/System.out.
    • Plusy: Nie korzystamy z bibliotek stron trzecich.
    • Minusy: Ścieżka nie jest dla początkujących. Względna złożoność implementacji testu; ilość kodu w teście może być większa niż w testowanym zadaniu.
  3. Korzystanie z dodatkowych bibliotek do testów.
    • Plusy: Czysty kod w testach, względna łatwość pisania testu. Kod źródłowy testowanej klasy nie ulega zmianie.
    • Wady: Konieczność podłączenia bibliotek innych firm do projektu.
Szczerze mówiąc, najbardziej podobała mi się opcja trzecia, więc spróbujmy ją wdrożyć.
Zasady systemu
Krótkie wyszukiwanie doprowadziło mnie do strony http://stefanbirkner.github.io/system-rules/ i od razu stało się jasne, że tego właśnie potrzebowałem.
Zbiór reguł JUnit do testowania kodu korzystającego z Java.lang.System.
Zatem pobierzmy bibliotekę . Pobierz bibliotekę Commons IO wymaganą do działania reguł systemowych . Łączymy obie biblioteki z naszym projektem (Plik -> Struktura projektu -> Biblioteki -> + -> Java) i rozpoczynamy rzeźbienie: Po uruchomieniu nasze zadanie prosi Cię o wprowadzenie liczb N+1 z konsoli, gdzie pierwsza liczba mówi Ci ile numerów będzie za nim podążać. W System Rules do tego celu wykorzystywana jest klasa TextFromStandardInputStream.Na początek musimy dodać do naszej klasy testowej pole tego typu i oznaczyć je adnotacją @Rule: @Rule public final TextFromStandardInputStream systemInMock = emptyStandardInputStream(); Następnie bezpośrednio w metodzie testowej wskazujemy niezbędne dane: systemInMock.provideText("4\n2\n6\n1\n3\n"); Jak widać, liczby są przesyłane w formie tekstowej i są oddzielone ciągami łączników „\n”. Na tej podstawie okazuje się, że N będzie równe 4, a maksimum będziemy szukać z liczb {2, 6, 1, 3}. Następnie musimy utworzyć instancję testowanej klasy i wywołać metodę main(). Nasz program odczytuje dane z systemInMock, przetwarza je i wypisuje wynik, a my wystarczy go odczytać i porównać ze standardem. W tym celu reguły systemowe udostępniają nam klasę StandardOutputStreamLog. Dodajemy pole określonego typu: @Rule public final StandardOutputStreamLog log = new StandardOutputStreamLog(); Wydrukowane dane można odczytać metodą .getLog(), natomiast trzeba uwzględnić obecność znaków nowej linii, finalne opcje mogą wyglądać następująco: assertEquals("{2, 6, 1, 3}, max = 6", "6", log.getLog().trim()); // Lub assertEquals("{2, 6, 1, 3}, max = 6", "6\r\n", log.getLog()); Pomiędzy testami, aby unikaj nakładania się danych, musisz wyczyścić dziennik log.clear(); Pełny tekst mojej klasy testowej: import org.junit.Rule; import org.junit.Test; import org.junit.contrib.java.lang.system.StandardOutputStreamLog; import org.junit.contrib.java.lang.system.TextFromStandardInputStream; import static org.junit.Assert.*; import static org.junit.contrib.java.lang.system.TextFromStandardInputStream.emptyStandardInputStream; public class UtilAppTest { @Rule public final TextFromStandardInputStream systemInMock = emptyStandardInputStream(); @Rule public final StandardOutputStreamLog log = new StandardOutputStreamLog(); @Test public void testAddition() throws Exception { systemInMock.provideText("4\n2\n6\n1\n3\n"); UtilApp utilApp = new UtilApp(); utilApp.main(new String[]{}); assertEquals("{2, 6, 1, 3}, max = 6", "6", log.getLog().trim()); systemInMock.provideText("5\n-100\n-6\n-15\n-183\n-1\n"); log.clear(); utilApp.main(new String[]{}); assertEquals("{-100, -6, -15, -183, -1}, max = -1", "-1", log.getLog().trim()); systemInMock.provideText("3\n2\n0\n-1\n"); log.clear(); utilApp.main(new String[]{}); assertEquals("{2, 0, -1}, max = 2", "2", log.getLog().trim()); } } Uruchamiamy i cieszymy się. -=!!! WAŻNE!!!=- Ten materiał ma charakter WYŁĄCZNIE informacyjny, nie gwarantuję pomyślnego przetestowania zadania na serwerze, jeżeli w pakiecie z zadaniem znajduje się dodatkowa klasa. Przed wysłaniem zadania do weryfikacji na serwer usuń wszystko, co zbędne: niepotrzebne pliki, niepotrzebne klasy, zakomentowany kod. Pomyślne zakończenie utworzonych testów nie gwarantuje pomyślnego zakończenia testów na serwerze. Celowo nie przeżułem materiału teoretycznego: teoria testów jednostkowych, adnotacje JUnit, twierdzenie itp., cały materiał znajduje się w linkach podanych w tekście. Być może macie swoje własne sposoby testowania zadań, chętnie omówię je z Wami w komentarzach.
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION