Rozpocząłem moją przygodę od uruchomienia mvn clean cobertura:cobertura w ramach projektu mavenowego (wspomniałem, że był to moduł OpenEJB - openejb-ejbd, ale tak na prawdę nie ma to większego znaczenia).
jlaskowski@work /cygdrive/c/oss/openejb3/server/openejb-ejbdUruchomienie polecenia wykonuje testy oraz za pomocą wtyczki cobertura-maven-plugin następuje badanie pokrycia kodu źródłowego testami. W wyniku, w katalogu target/site/cobertura, znajduje się dokumentacja HTML z prezentacją pokrycia testami.
$ mvn clean cobertura:cobertura
[INFO] Scanning for projects...
[INFO] Searching repository for plugin with prefix: 'cobertura'.
[INFO] ------------------------------------------------------------------------
[INFO] Building OpenEJB :: Server :: EJBd
[INFO] task-segment: [clean, cobertura:cobertura]
[INFO] ------------------------------------------------------------------------
...
[INFO] [cobertura:cobertura]
[INFO] Cobertura 1.9 - GNU GPL License (NO WARRANTY) - See COPYRIGHT file
Cobertura: Loaded information on 13 classes.
Report time: 594ms
[INFO] Cobertura Report generation was successful.
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ------------------------------------------------------------------------
jlaskowski@work /cygdrive/c/oss/openejb3/server/openejb-ejbdOtworzenie strony index.html prezentuje następujący raport:
$ ls -l target/site/cobertura/
cobertura/ org.apache.openejb.server.ejbd.AuthRequestHandler.html
css/ org.apache.openejb.server.ejbd.BasicClusterableRequestHandler.html
frame-packages.html org.apache.openejb.server.ejbd.CallContext.html
frame-sourcefiles-org.apache.openejb.server.ejbd.html org.apache.openejb.server.ejbd.ClientObjectFactory.html
frame-sourcefiles.html org.apache.openejb.server.ejbd.ClusterableRequestHandler.html
frame-summary-org.apache.openejb.server.ejbd.html org.apache.openejb.server.ejbd.DeploymentIndex.html
frame-summary.html org.apache.openejb.server.ejbd.EjbDaemon.html
help.html org.apache.openejb.server.ejbd.EjbRequestHandler.html
images/ org.apache.openejb.server.ejbd.EjbServer.html
index.html org.apache.openejb.server.ejbd.JndiRequestHandler.html
js/ org.apache.openejb.server.ejbd.ServerSideResolver.html
Widać na nim, że jedynie klasa BasicClusterableRequestHandler jest pokryta w 100% przez testy, a pozostałe prezentują się marnie. Pokrycie w 100% nie powinno być celem samym w sobie, ale dla mnie najważniejsze było, aby zacząć, więc akurat wybrałem moduł openejb-ejbd, w którym wybrałem klasę DeploymentIndex (obecnie ma 59% pokrycia, ale jeszcze wczoraj było 0!). Sama prezentacja pokrycia w postaci zielonych i czerwonych miejsc wskazuje, które są objęte testami, a które wymagają troski.
Po rozmowie z innym programistą projektu OpenEJB i jego liderem - Davidem Blevinsem - okazało się, że:
<jlaskowski> write...I'm all ears (trying to polish a junit test for DeploymentIndex)...czyli klasa nie jest w ogóle w użyciu. No cóż pierwsze podejście nie musi być idealne ;-) Mimo wszystko dobrze było rozpocząć rozpoznawanie kodu źródłowego OpenEJB wybierając dowolną klasę, której pokrycie jest bliskie zeru, a że nie jest w użyciu jeszcze - nic straconego. Od razu Dave przyszedł mi jednak z pomocą i napisał:
<dblevins> we don't use the DeploymentIndex anymore
<jlaskowski> what?!
<dblevins> kind of an unfortunate place to start, but yea, it's cruft at the moment
<dblevins> I suggest the Assembleri Dave rozwinął jak to tam w środku się dzieje. Mam się czym zająć w wolnych chwilach i mam pewność, że dana klasa jest w użyciu. Jestem ocalony!
<jlaskowski> org.apache.openejb.assembler.classic.Assembler?
<dblevins> right
<jlaskowski> k
<dblevins> and the ConfigurationFactory
<dblevins> the flow of the system startup is this...
Co to jednak ma wspólnego z refactoringiem?! Podczas pracy z klasą testową - DeploymentIndexTest - rozpocząłem niewinnie z pojedyńczą metodą, która później rozwinęła się w dwie, a później dodałem metodę setUp(), która ustawia środowisko (=zmienne) i jest wykonywana każdorazowo przed każdym testem - metoda oznaczona adnotacją @Before. Okazało się, że wbrew temu jak do tej pory podchodziłem do sprawy modyfikacji klas metodą Ctrl-C/Ctrl-V, tym razem postanowiłem spróbować przyzwyczaić się do kolejnego elementu środowisk IDE (korzystałem z Eclipse IDE 3.4M6) i podczas zmiany rangi zmiennej lokalnej do poziomu zmiennej instancji skorzystałem z menu Refactor > Convert Local Variable to Field...
Po chwili miałem temat z głowy. Wystarczy umiejscowić kursor na zmiennej lokalnej i wybrać menu, aby przenieść ją na poziom zmiennej instancji. Przydała się również funkcja Refactor > Rename (Alt+Shift+R), która jednak była już kilkakrotnie przeze mnie wykorzystywana. Piszę o tym, gdyż mając w nawyku pracę z różnej maści edytorami byłem przyzwyczajony do ciągłego stosowania techniki Copy-Paste, która była uniwersalna, i mimo wiedzy na temat funkcjonalności Refactoring w Eclipse (również NetBeans czy IntelliJ IDEA) nie miałem nawyku jej stosowania tracąc czas na rzeczy, które nie były ani ciekawe, ani miłe. Zdecydowanie polecam tego typu podejście do zmiany struktury kody, niż owe Copy-Paste.
W trakcie poznawania zmian w JUnit 4 natrafiłem na sposób deklarowania oczekiwanego wyjątku w teście. Do tej pory stosowałem technikę:
try {W JUnit 4 zalecanym sposobem jest użycie atrybutu expected adnotacji @Test, np. @Test(expected= IndexOutOfBoundsException.class), czyli to, co zostało zaprezentowane wyżej teraz powinno wyglądać następująco:
...kod, który zgłaszał wyjątek...
fail("Powinien zostać zgłoszony wyjątek NazwaWyjątkuException");
} catch (NazwaWyjątkuException expected) {
// ignored
}
@Test(expected=NazwaWyjątkuException.class)Więcej informacji o JUnit w JUnit Cookbook. Warto się z nim zapoznać, bo zawiera kwintesencję JUnit i jego przeczytanie nie powinno zabrać więcej niż 5-10 minut (pewnie przeczytanie tego wpisu zajęło więcej ;-)). Wracam do testów jednostkowych...
public void mojTest() {
...kod, który zgłaszał wyjątek...
}
Pytanie konkursowe: W jaki sposób deklarujemy oczekiwany wyjątek w JUnit 4?
Popraw mnie jeżeli sie pogubiłem ale pytasz o to co właśnie wcześniej opisałeś. Zatem jeżeli chodziło Ci o przetestowanie "nieoczekiwanego" wyjątku to pewnie powinno być to coś takiego:
OdpowiedzUsuń@Test
public void mojTest()
throws NazwaWyjątkuException.class
{
...kod, który zgłaszał wyjątek...
}
...choć słowo "nieoczekiwany" brzmi tu jak "nieoczekiwana kontrola" u Barei skoro deklarujemy w sygnaturze metody nazwę wyjątku :)
Pzdr,
Pawel
Dokładnie o to chodziło, gdyż zwykłem zadawać pytania z tematu, który prezentuję. Twoja odpowiedź jest poprawna. Zaliczone.
OdpowiedzUsuńZ "tym nieoczekiwanym" również masz rację, gdyż w tym przypadku jak najbardziej oczekiwaliśmy wystąpienia wyjątku, więc nie może być mowy o jego "nieoczekiwaniu". Dzięki za zwrócenie na to uwagi.
Jacek