Obsah
Ladění programů s debuggerem jdb
Nástroje ověřování podmínek za běhu - klíčové slovo assert
Nástroje testování jednotek (tříd, balíků) - junit
Pokročilé systémy dynamického ověřování podmínek - jass
je používat systémy pro běhovou kontrolu platnosti podmínek:
vstupní podmínka metody (zda je volána s přípustnými parametry)
výstupní podmínka metody (zda jsou dosažené výstupy správné)
a podmínka kdekoli jinde - např. invariant cyklu...
K tomuto slouží jednak
standardní klíčové slovo (od JDK1.4) assert booleovský výraz
testovací nástroje typu JUnit (a varianty - HttpUnit,...) - s metodami assertEquals() apod.
pokročilé nástroje na běhovou kontrolu platnosti invariantů, vstupních, výstupních a dalších podmínek - např. jass (Java with ASSertions), http://csd.informatik.uni-oldenburg.de/~jass/.
Ukázka testu jednotky:Test třídy ChovatelPsu
Napsat zdrojový program užívající klíčové slovo assert (pouze od verze Java2 v1.4 výše). Nepotřebujeme žádné speciální běhové knihovny, vše je součástí Javy; musíme ovšem mít překladové i běhové prostředí v1.4 a vyšší.
Přeložit jej s volbou -source 1.4
Spustit jej s volbou -ea (-enableassertions).
Aktivovat aserce lze i selektivně pro některé třídy (-ea název_třídy nebo -ea název_balíku... - tři tečky na konci!!!).
Dojde-li za běhu programu k porušení podmínky stanovené za assert, vznikne běhová chyba (AssertionError) a program skončí.
Třída Zlomek používá assert k ověření, že zlomek není (chybou uživatele) vytvářen s nulovým jmenovatelem.
Za assert uvedeme, co musí v daném místě za běhu programu platit.
Spustíme-li bez povolení assert (bez volby -ea nebo naopak s explicitním zákazem: -da), pak program podmínky za assert neověřuje:
Uvědomit si, že žádný nástroj za nás nevymyslí, JAK máme své třídy testovat. Pouze nám napomůže ke snadnějšímu sestavení a spuštění testu.
Stáhnout si z http://junit.org poslední (stačí binární) distribuci testovacího prostředí.
Nainstalovat JUnit (stačí rozbalit do samostatného adresáře).
Napsat testovací třídu/třídy - buďto implementují rozhraní junit.framework.Test nebo obvykleji rovnou rozšiřují třídu junit.framework.TestCase
Testovací třída obsahuje metodu na nastavení testu (setUp), testovací metody (testNeco) a úklidovou metodu (tearDown).
Testovací třídu spustit v prostředí (řádkovém nebo GUI) - junit.textui.TestRunner, junit.swingui.TestRunner...
Testovač zobrazí, které testovací metody případně selhaly.
Třída Zlomek zůstává zhruba jako v předchozím příkladu, přibývají však metody equals (porovnává dva zlomky, zda je jejich číselná hodnota stejná) a soucet (sečítá dva zlomky, součet vrací jako výsledek).
Spuštění testovače v prostředí GUI Swing nad testovací třídou JUnitDemo.
Pokud testovací třída prověří, že testovaná třída/y je/jsou OK, vypadá to přibližně takto:
jass je preprocesor javového zdrojového textu. Umožňuje ve zdrojovém textu programu vyznačit podmínky, jejichž splnění je za běhu kontrolováno.
pre- a postconditions u metod (vstupní a výstupní podmínky metod)
invarianty objektů - podmínky, které zůstávají pro objekt v platnosti mezi jednotlivými operacemi nad objektem
stažení a instalace balíku z http://csd.informatik.uni-oldenburg.de/~jass/
vytvoření zdrojového textu s příponou .jass, javovou syntaxí s použitím speciálních komentářových značek
takový zdrojový text je přeložitelný i normálním překladačem javac, ale v takovém případě ztrácíme možnosti jass
proto nejprve .jass souboru převedeme preprocesorem jass na javový (.java) soubor
ten již přeložíme javac a spustíme java, tedy jako každý jiný zdrojový soubor v Javě
z .jass zdrojů je možné vytvořit také dokumentaci API obsahující jass značky, tj. informace, co kde musí platit za podmínky atd. - vynikající možnost!
úvodní materiálek k použití junit (v němčině, jako PDF)
Operátory v Javě: aritmetické, logické, relační, bitové
Porovnávání primitivních hodnot a objektů, metody equals a hashCode
Ternární operátor podmíněného výrazu
Typové konverze
Operátor zřetězení
& (nepodmíněný - vždy se vyhodnotí oba operandy),
&& (podmíněný - líné vyhodnocování - druhý operand se vyhodnotí, jen nelze-li o výsledku rozhodnout z hodnoty prvního)
Tyto lze použít na porovnávání primitivních hodnot:
Test na rovnost/nerovnost lze použít na porovnávání primitivních hodnot i objektů:
pozor na porovnávání objektů: == vrací true jen při rovnosti odkazů, tj. jsou-li objekty identické. Rovnost obsahu (tedy "rovnocennost") objektů se zjišťuje voláním metody o1.equals(Object o2)
pozor na srovnávání floating-points čísel na rovnost: je třeba počítat s chybami zaokrouhlení; místo porovnání na přesnou rovnost raději používejme jistou toleranci: abs(expected-actual) < delta
Porovnáme-li dva objekty (tzn. odkazy na objekty) prostřednictvím operátoru == dostaneme rovnost jen v případě, jedná-li se o dva odkazy na tentýž objekt - tj. dva totožné objekty.
Jedná-li se o dva obsahově stejné objekty existující samostatně, pak == vrátí false.
Chceme-li (intuitivně) chápat rovnost objektů podle obsahu, tj.
dva objekty jsou rovné (rovnocenné, nikoli totožné), mají-li stejný obsah, pak
musíme pro danou třídu překrýt metodu equals, která musí vrátit true, právě když se obsah výchozího a srovnávaného objektu rovná.
Fungování equals lze srovnat s porovnáváním dvou databázových záznamů podle primárního klíče.
Nepřekryjeme-li equals, funguje původní equals přísným způsobem, tj. rovné si budou jen totožné objekty.
Příklad: objekt třídy Clovek nese informace o člověku. Dva objekty položíme stejné (rovnocenné), nesou-li stejná příjmení:
Obrázek 1.10. Dva lidi jsou stejní, mají-li stejná příjmení
public class Clovek implements Comparable {
String jmeno, prijmeni;
public Clovek (String j, String p) {
jmeno = j;
prijmeni = p;
}
public boolean equals(Object o) {
if (o instanceof Clovek) {
Clovek c = (Clovek)o;
return prijmeni.equals(c.prijmeni);
} else
throw new IllegalArgumentException(
"Nelze porovnat objekt typu Clovek s objektem jineho typu");
}
}
}
Méně agresivní verze by nemusela při porovnávání s jiným objektem než Clovek vyhodit výjimku, pouze vrátit false.
Jakmile u třídy překryjeme metodu equals, měli bychom současně překrýt objektů i metodu hashCode:
hashCode vrací celé číslo (int) „co nejlépe“ charakterizující obsah objektu, tj.
pro dva stejné (equals) objekty musí vždy vrátit stejnou hodnotu.
Pro dva obsahově různé objekty by hashCode naopak měl vracet různé hodnoty (ale není to stoprocentně nezbytné a ani nemůže být vždy splněno). Metoda hashCode totiž nemůže vždy být prostá.
V těle hashCode s oblibou „přehráváme“ (delegujeme) řešení na volání hashCode jednotlivých složek objektu - a to těch, které figurují v equals:
Obrázek 1.11. Třída Clovek s metodami equals a hashCode
public class Clovek implements Comparable {
String jmeno, prijmeni;
public Clovek (String j, String p) {
jmeno = j;
prijmeni = p;
}
public boolean equals(Object o) {
if (o instanceof Clovek) {
Clovek c = (Clovek)o;
return prijmeni.equals(c.prijmeni);
} else
throw new IllegalArgumentException(
"Nelze porovnat objekt typu Clovek s objektem jineho typu");
}
public int hashCode() {
return prijmeni.hashCode();
}
}
vlevo << o stanovený počet bitů
vpravo >> o stanovený počet bitů s respektováním znaménka
vpravo >>> o stanovený počet bitů bez respektování znaménka
Dále viz např. Bitové operátory
Platí-li první operand (má hodnotu true) ->
Typ prvního operandu musí být boolean, typy druhého a třetího musí být přiřaditelné do výsledku.
Podobně jako v C/C++
Píše se (typ)hodnota, např. (Clovek)o, kde o byla proměnná deklarovaná jako Object.
Pro objektové typy se ve skutečnosti nejedná o žádnou konverzi spojenou se změnou obsahu objektu, nýbrž pouze o potvrzení, že běhový typ objektu je požadovaného typu - např. (viz výše) že o je typu Clovek.
Naproti tomu u primitivních typů se jedná o úpravu hodnoty - např. int přetypujeme na short a „ořeže“ se tím rozsah.