PRA02.rst

Pracownia Programowania

Debugging w Java + testy

Uwaga! cały kod do Zadania 7 włącznie znajduje się na gałęzi TestAndDebugStart w repozytorium https://github.com/WitMar/PRA2018-2019 . Kod końcowy w gałęzi TestAndDebugEnd.

Jeżeli nie widzisz odpowiednich gałęzi na GitHubie wykonaj Ctr+T, a jak to nie pomoże to wybierz z menu VCS->Git->Fetch.

Podsumowanie zajęć I

Co wiemy po pierwszych zajęciach:

* Projekt podzielony jest na katalogi źródłowy - src, testowy - test, zasobów - resources.
* W katalogu projektu umieszczony jest plik pom.xml odpowiadający za ustawienia Mavena, w pliku tym dodajemy odnośniki do zewnętrznych bibliotek i ustawienia budowania aplikacji.
* Ustawienia loggera znajdziemy w pliku log4.properties w katalogu resources. W przypadku gdy inna biblioteka będzie potrzebowała ustawień będziemy także szukać i dodawać je w katalogu resources w odpowiednio nazwanym pliku.
* Pracujemy na GIT - nowa funkcjonalność (nowy temat zajęć) zakładana jest na nowej gałęzi (ang. branch).

Synchronizacja kodu Git z oryginalnym repo

Przejdź do VCS -> Git -> Remotes

Dodaj wpis o repozytorium prowadzącego

nazwij je najlepiej jako PrzedmiotoweRepo

Wybierz Ctrl + T, aby odświeżyć zależności.

Jeżeli Ctrl+T z jakiegoś powodu nie ściągnie oczekiwanych zmian, to żeby wymusić ściągnięcie zmian wybierz VCS -> Git -> fetch.

Wtedy na liście branchy w prawym dolnym rogu ekranu powinieneś widzieć gałęzie z repo przedmiotowego i ze swojego.

Przejdź na gałąź TestAndDebugStart.

Jako że dodaliśmy nowe repozytorium, możliwe, że musisz ponownie skonfigurować mavena. Przejdź do zakładki Maven i sprawdź, czy widzisz goale (Lifecycle, Plugin, Dependencies), jeżeli nie, spróbuj odświeżyć Mavena (dwie strzałki w kółko). Jeśli to nie pomoże wybierz mały plus i wskaż plik pom.xml z dysku twardego. Następnie odśwież ponownie Mavena. To powinno pomóc - sprawdź, czy twoje pliki .java mają zielone kółko obok nich w zakładce Project.

mavenTryAgain.png

Przydatne skróty IdeaJ

Lista skrótów IdeaJ

Alt + Enter pokaż podpowiedź rozwiązania błędu

Ctrl + W, Ctrl + Shift + W zaznaczenie całej sekcji, rozszerzenie zaznaczenia, zawężenie zaznaczenia.

Ctrl + Shift + N wyszukanie pliku po nazwie

Shift + Shift wyszukiwanie wszędzie

Alt + Insert generuj kod

Ctrl + / zakomentuj line

Ctrl + Shift + / odkomentuj linie

Ctrl + E ostatnio otwierane pliki

Ctrl + Alt + L formatuj kod

Tab, Shift+Tab wcięcie, cofnij wcięcie

Shift + F6 zmiana nazwy

Ctrl + Shift + Alt + T refaktoruj

Ctrl + Alt + M wyekstraktuj metodę

Alt + Right/Left przełączaj zakładki

Ctrl + (klik na nazwie metody) pokaż użycie

Ctrl + Alt + (klik na nazwie metody) pokaż implementację

Ctrl + K służy do commitowania kodu lokalnie do repozytorium

Ctrl + Shift + K służy do commitowania kodu do zewnętrznego repozytorium

Ctrl + T służy do odświeżania projektu - ściągania zmian z serwera

Wykonaj

Przedź na branch TestAndDebugStart

Wyszukaj klasę o nazwie ClassThatHaveItAll (Ctrl + Shift + N).

Zformatuj kod (Ctrl + Alt + L) - na wydziałowym linuxie ten skrót oznacza wyloguj, musisz zmienić skrót w ustawieniach systemu żeby móc go używać.

Dodaj do klasy w nagłówku linijkę

List <Long> list;

Wybierz automatyczną poprawę błędu (Alt + Enter).

Następnie wybierz (Alt + Insert) wygeneruj konstruktor, oraz settery i gettery dla klasy.

Znajdź interfejs InterfaceOne (Ctr + Shift + f). Wyszukaj użycie i implementacje metody printMe() (Ctrl + click na nazwie metody) klikając na implementacje jak i definicje metody.

Debug

Poprzez wybór Run -> Debug lub wybierając na klasie prawym przyciskiem myszy Debug możemy debugować nasz kod.

Aby uruchomić kod lub uruchomić debuggowanie możesz też kliknąć na zielony trójkąt obok nazwy klasy.

hereForDebug.png

Pomocne w debugowaniu są tzw. breakpointy, czyli miejsce w kodzie, w którym chcemy zatrzymać wywołanie kodu by móc podejrzeć wartości zmiennych i stan programu. Żeby dodać breakpoint klikamy obok numeru linii tak, by pojawiła się czerwona kropka.

breapoint.png

Po kliknięciu prawym przyciskiem myszy możemy określić także specjalny warunek przy breakpoincie.

warunek.png

Po uruchomieniu trybu debug na dole pojawia się nam okno debugowania na którym wyświetlone są aktualne wartości zmiennych. Dodatkowo możemy wybrać przez Alt+F8 lub klikając na ikonę kalkulatora okno ewaluacji wyrażeń i wykonać ewaluację jakiegoś wyrażenia na obecnych wartościach zmiennych. Ewaluacja wyrażeń jest przydatna przy debugowaniu np. wartości list, których wartości nie jesteśmy w stanie przejrzeć pojedynczo.

debug.png

Przy odpowiednich ustawieniach możliwe jest także debugowanie aplikacji działających na serwerze w czasie wykonywania (więcej na ten temat na przyszłych zajęciach).

Wykonaj

Uruchom klasę Breakpoints. Jaki błąd wystąpił?

Uruchom debug klasy Breakpoints.

Żeby zatrzymać wywołanie dodaj breakpoint w klasie.

Użyj F8 (lub strzałka w dół na panelu debugingu) aby przejść w wywołaniu kodu linijka po linijce.

Użyj StepInto F7 (strzała w prawo-dół na panelu debugingu) aby wejść w wywołanie metody.

Znajdź i popraw błąd.

Wykonaj

Uruchom klasę EvaluateExpressions. Jaki błąd wystąpił?

Ustaw breakpointa w metodzie ProcessElementAtIndex.

Otwórz okno ewaluacji wyrażeń i wpisz w nie list.get(index) .

Przechodź F9 (zielony trójkąt na panelu debug) do kolejnych wystąpień breakpointa i podglądaj wartości zmiennych.

By sprawdzić jak naprawdę wyglądają ostatnie elementy listy wpisz w oknie evaluacji następujący kod:

Collections.reverse(list);
list

Znajdź i popraw błąd.

Wykonaj

Uruchom klasę ConditionalBreak. Jaki błąd wystąpił?

W linii 18, ustaw zwykły breakpoint, czy łatwo jest znaleźć błąd?

Ustaw w linii 18 warunkowy breakpoint tak, by zatrzymywał się przy spełnieniu warunku !EverythingIsOk.

Jak widzisz breakpoint nie działa, przenieś go do linii 19.

Uruchom kod, co wywołało błąd?

JUnit

JUnit to biblioteka służąca do pisania testów kodu. Testy mają na celu kontrolę jakości kodu, różnych ścieżek wywołań a także tego czy nowe zmiany nie wprowadzają błędów i zachowują starą funkcjonalność.

Proces budowania wersji przez Maven domyślnie uruchamia wszystkie testy w procesie tworzenia pliku wykonywalnego.

Najpierw musimy dodać w pliku pom.xml zależność dla modułu testowego.

W naszym branchu jest już stworzona klasa testowa jednak by dodać nową należałoby wykonać poniższe operacje:

Wejdź do klasy AdvanceMath i wybierz Ctrl + Shift + T , kliknij create new Test. Aby osiągnąć to samo inaczej można wybrać nazwę klasy i kliknąć Alt + Enter i wybrać create test.

Biblioteka JUnit korzysta z tzw. adnotacji. Przed każdą metodą która ma być uruchamiana jako test umieszczamy @Test.

Inną istotną adnotacją jest @Before która pozwala nam zdefiniować operacje które będą wykonywane przed uruchomieniem każdego testu.

Przykładowa klasa testująca:

package second.junit;

import com.sun.xml.internal.ws.policy.AssertionSet;
import example.HelloWorld;
import org.apache.log4j.Logger;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

import static org.junit.Assert.*;

public class AdvanceMathTest {

    AdvanceMath math;
    final static Logger logger = Logger.getLogger(AdvanceMath.class);

    @Before
    public void setUp(){
        logger.info("Odpalam setUpa");
        math = new AdvanceMath();
    }

    @Test
    public void additionTest() {
        Integer a = math.addition(1,4);
        assertTrue(a==5);
    }

    @Test
    public void additionTestString() {
        long a = math.addition("1",4);
        Assert.assertEquals(5L, a);
    }


    @Test(expected = Exception.class)
    public void additionTestString2() {
        int a = math.addition("a1",4);
    }

}

Test możemy uruchomić klikając na klasę i wybierając run lub przez zakładkę Maven -> Test. Testy są też dodawane automatycznie przy wykonaniu Install w zakładce Mavena.

Przykłady wykorzystania biblioteki JUnit :

Polecam spojrzeć na przykłady na temat testowania list i map.

Projekt z zakresu 1-3

Stworzenie programu, który będzie odczytywał dane z pliku / konsoli i zapisywał je do pliku.

Program powinien wykorzystywać bibliotekę Javy dostępną w repozytorium Maven (może być to np. biblioteka do przetwarzania tekstu, obrazu, przetwarzania danych, sztucznej inteligencji itp. - dowolna).

Użytkownik na początku pracy programu powinien podawać korzystając z konsoli numer PESEL. Numer PESEL powinien być sprawdzany względem poprawności - co najmniej w zakresie sumy kontrolnej ostatniej cyfry.

W przypadku złego numeru PESEL program nie działa / nie przechodzi dalej zanim użytkownik nie poda poprawnego numeru.

Napisz testy do klas - dla metody sprawdzającej poprawność numeru PESEL oraz dla jakiejś innej klasy z twojego projektu. W projekcie użyj strumieni oraz co najmniej dwóch struktur danych (set,map,list,stack itp.) w tym strumienia którego wynikiem jest struktura danych oraz strumienia który nie zwraca wyniku. Wykorzystanie struktur nie musi być "sensowne" ma pokazać tylko, że potraficie Państwo ich używać.

*

Wykorzystano materiały z:

https://imagej.net/Debugging_Exercises

Dobre praktyki pisania kodu

  • Make indents inside loops and if-statements

function foo() {
    if ($maybe) {
        do_it_now();
        again();
    } else {
        abort_mission();
    }
    finalize();
}
  • You can deal with curly brackets also like this:

function foo()
{
    if ($maybe)
    {
        do_it_now();
        again();
    }
    else
    {
        abort_mission();
    }
    finalize();
}
  • Use blank lines consistently and as required. Blank lines may be used for separating code lines or line groups semantically for readability.

  • Character count of a line should be limited for readability.

  • Name variables, procedures, functions in sensible way, start with small letter and introduce new word with capital letter

studentsCounter;
listIterator;
averageOverLastWeek;
findBestInClass();
computeAverage();
  • Name classes in sensible way, start with capital letter and introduce new word with capital letter

NightShift;
FastCar;
  • Name constant values with all capital letters, separate words with _

DAYS_IN_THE_WEEK();
NUMBER_OF_SHIFTS();
  • Using space chars in code should also be consistent in whole application. Generally, situations below are suitable for using spaces:

  • Between operators and operands:

a += b , c = 0; (a == b)
  • Between statement keywords and brackets:

if (value) {, public class A {
  • After ';' char in loops:

for (int i = 0; i < length; i++)
  • Between type casters and operands:

(int) value , (String) value