Servlet yksikkötestausta Spring mockeilla Maven + EE 6 ympäristössä

Jahas, tulipa taas käytettyä aika detaileihin ja tapeltua hassujen pikku yksityiskohtien parissa. Tavoite oli tehdä servlet komponentille yksikkötestausta TDD hengessä, ja sitä varten se haluttiin eristää kaikesta muusta, mm. EJB ja JPA kerroksista, jopa niin että servereitä ei käynnistetä. Teoriassahan homma menee näin:

– Tee JUnit tai TestNG tms testirunko haluamallasi tavalla

– Instansio Servlet ja injektoi sille kaikki mitä se tarvitsee toimiakseen – käytännössä esim. kutsut setXX metodeita joilla asetat esim. mahdollisen tilattoman ejb viittauksen jäsenmuuttujaan. Periaatteessa kaikki oliot mitä servlet hakee/tarvitsee toimiakseen tulisi asettaa sille mockeina. Voit tehdä mockit itse anonyymillä luokalla joka toteuttaa rajapinnan, tai voit käyttää esim. easymock tai mockito kehyksiä joista kirjoittelin jo aiemmin. Jos olet kovakoodannut viittauksia JNDI:hin, tough luck. Ne pitää sitten purkaa ulos, jos servlettisi aktiivisesti hakee resurssiviittauksia sitä ei voi käytännössä yksikkötestata.

– Varsinaiseen kutsuun tarvitset myös mockit Request ja Response olioista – olen tässä huomannut Spring test-moduulista löytyvien mockien olevan korvaamaton apu. Luodaan ne, konfiguroidaan ne, ja sitten annetaan ne doGet kutsun parametrina

– Lopuksi tutkitaan request ja response olioista onnistuiko kutsu. Mistä sen tietää? Riippuu mitä servlet tekee. Tulostettiinko sivulle jotain? Asetettiinko requestiin muuttujia? Tuliko poikkeuksia?

Näin siis periaatteessa homma menee. Tässä esimerkkikoodia:

 @Test
     public void testDoGet() throws ServletException, IOException {

         // set up SUT
         HelloServlet hs = new HelloServlet();

         // set up mocks SUT needs, add some request (form) parameters
         MockHttpServletRequest request = new MockHttpServletRequest("GET","/HelloServlet");
         request.addParameter("name", "Johnny");

         MockHttpServletResponse response = new MockHttpServletResponse();

         // test SUT
         hs.doGet(request,response);

         // validate results
         String result = response.getContentAsString();
         assertTrue(result.contains("Johnny"));
         assertTrue(result.contains("Hello"));

     }

Tässä ei ole siis EJB viittauksia tai muita resursseja, vaan mittaillaan ihan perinteisen helloworld servletin rakennetta. Ongelma tuli tätä testatessa mavenissä. Sain aika metkan virheilmoituksen:

java.lang.ClassFormatError: Absent Code attribute in method that is not native or abstract in class file javax/servlet/ServletException

Mistäpä moinen johtuu? Näemmä Maven riippuvuudesta jolla noudetaan sinänsä kätevästi EE 6 kirjastot kaikki kerralla, tämmöinen:

        <dependency>
            <groupId>javax</groupId>
            <artifactId>javaee-web-api</artifactId>
            <version>6.0</version>
            <scope>provided</scope>
        </dependency>

Kävikin ilmi että tämä on paholaisesta. Se hakee näennäisen hyvät servlet.jar paketit mukaan polkuun joilla käännös onnistuu. Mutta joku suuren sarvipään itsensä kehittämä oikku on aiheuttanut sen että niistä on poistettu kaikki bytecode metodirungoista. Toisin sanoen, testaus ei niillä onnistu. Ja mikä hauskointa, jos tuot mukaan uusia kirjastoja jotka korjaavat ongelman, niin tämä riippuvuus siltikin aiheuttaa saman virheen.

Eli ratkaisu. Poistetaan tämä riippuvuus. Korvataan yksittäisillä riippuvuuksilla joilla tuodaan halutut ee piirteet. Tässä on oma ongelmansa: yritäppä löytää geneeristä jpa 2.0 rajapintakirjastoa jostain yleiseltä suurelta palvelimelta.. Mutta käytännössä nappaat mukaan servlet, ejb 3.1, ja jpa 2.0 kirjastot, ja ympäristö on taas ok. Clean and build vielä päälle. Nappasin itse yhden servlet kirjaston joka toimii, muitakin voi toki käytellä. Joskus voisin ihmetellä voiko jollain mekanismilla ylikirjoittaa vain tuon servlet osan ja tuoda muut yleisellä importilla mutta ainakaan vielä en keksinyt keinoa netistä.

Anyway, täältä siis toimiva servlet jar, ja Spring mockit:

   <repositories>
        <repository>
            <id>glassfish-repository</id>
            <url>http://download.java.net/maven/glassfish</url>
        </repository>

    </repositories>  

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>3.0.6.RELEASE</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>3.0.6.RELEASE</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.glassfish</groupId>
            <artifactId>javax.servlet</artifactId>
            <version>3.0</version>
            <scope>provided</scope>
        </dependency>

Edit: Ooooo Oraclella oli näemmä aika hyvä artikkeli EE 6 yksikkötestauksesta, jossa mm. käyty läpi tämä sama ongelma ja sen ratkaisu, hieman eri lailla. Tässä linkki: http://www.oracle.com/technetwork/articles/java/unittesting-455385.html

 

Pistän tähän varuiksi muistiin pääasian: Jos haluat Glassfish serverin kirjastot ympäristöön, muutkin kuin vain web-luokat, lisää tämä Maven riippuvuuksiin:

 
<dependency>
    <groupId>org.glassfish.extras</groupId>
    <artifactId>glassfish-embedded-all</artifactId>
    <version>3.1.1</version>
    <scope>provided</scope>
 </dependency>

Testattu ja toimii hienosti.

Mainokset

Glassfish, domain.xml ja merkistökoodaus

Jahas, tällä kertaa vähän vähemmän helposti lähestyttävä otsikko. Törmäsin tällä viikolla metkaan ominaisuuteen Glassfish serverissä. Sen tärkein konfiguraatiotiedosto, domain.xml, sisältää oikeastaan kaikki serverin säädöt. Sitä voi editoida käsin tai sitten hallintanäyttöjen kautta. Se on siinä mielessä outo xml tiedosto että siinä ei ole alussa xml prologia, deklaraatiota, ja näin ollen myöskään merkistökoodausta.

Ongelma ilmeni kun virittelin glassfishiin Active Directory autentikoinnin LDAP moduulin avulla. Moduuli nimittäin konfiguroidaan domain.xml tiedostoon, ja yksi arvoista on ryhmä josta tunnukset löytyvät.. Ja tässä tapauksessa ryhmän nimessä oli toki skandeja, josta ongelmat alkoivat. Glassfish ei suostu hevin boottaamaan kun tiedostosta löytyy outoja merkkejä, joten ensimmäinen vaisto oli tallettaa tiedosto UTF8 koodauksella ja lisätä deklaraatio kuten XML:n pitäisi toimia. Vaan ei toimi. Glassfish ei välitä tuon taivaallista deklaraatiosta eikä edes siedä sitä.

Seuraava yritys: Käytä XML unicode merkkejä, kuten ä (pieni ä-kirjain) tiedostossa. Nyt serveri käynnistyy hienosti. Mutta jos sen admin konsolin kautta tekee muutoksia, se tallettaa tiedoston taas jollain koodauksella, ja lataa toisella, eli uudelleenkäynnistys taas epäonnistuu.

Tästä käydään parrat päristen uskonsotaa laajemminkin, mutta tulos on epävarma. Versiossa 3.1 ei ainakaan vielä ratkaisua. Jännitystarinaa voi seurata osoitteessa http://java.net/jira/browse/GLASSFISH-16304

 

Löysin potentiaalisen asian mistä voi olla apua. Kun glassfish ei halua xml deklaraatiota totella (joka on paha virhe imho), vaan käyttää ympäristön oletuskoodausta, muutetaan sitten oletuskoodausta. Löysin ympäristömuuttujat JAVA_OPTS sekä JAVA_TOOL_OPTS, joille voi asettaa lisäparametreja joita virtuaalikone sitten käyttää. Asetin JAVA_TOOL_OPTS asentoon -Dfile.encoding=UTF8 – ja Glassfish näyttäisi nyt käynnistyessään käyttävän UTF8 merkistöä, jvm.log:in perusteella. En ole vielä varmentanut workaroundin toimivuutta koska käytössä ei ole erillistä testiympäristöä ja en ole aivan kauhean innostunut vasiten rikkomaan tuotantoympäristöä uudelleen, mutta ainakin tämä on parasta mihin nyt pystyy, ja aika näyttää sitten miten toimii. Tästä voi olla apua jos joku muu tuskailee glassfishin ja skandimerkkien kanssa.

Ensi kerralla taas sitten yleisempää löpinää, odotellessa Oracle Certified Enterprise Architect Master sertifikaatin tuloksia sekä JavaOne 2011 tapahtumaa 😉

Java 7 hiipi ulos

No niin, Java versio 7:ää onkin jo odotettu. Tyypillinen julkaisuväli Java versioilla oli n. 1-2 vuotta, mutta Java 7 on ollut työn alla viitisen vuotta. Sen oli alunperin tarkoitus olla vielä kunnianhimoisempi päivitys, mutta muutama suurempi palanen kuten Jigsaw ja Lambda siirrettiin versioon 8 joka tulee nyt pian perässä.

JDK 7 on toinen OpenJDK:hon perustuva Java, ja sen mukana tulee suuri kasa bugikorjauksia ja uusia rajapintoja, päivityksiä virtuaalikoneeseen, roskankeruuseen, jne. Yksi mielenkiintoinen osa on Project Coin (Smallest change..), jossa on jälleen kielen perusrakenteisiin puututtu. Paljon pieniä muutoksia jotka taas kumulativisoituvat. Yhteensä muutoksia on enemmän kuin aikanaan Java 5:ssa, mutta ne eivät ole niin laajavaikutteisia kuin aikanaan Generics ja Annotations. Mark Reinholdkin totesi että kyseessä ei ole ’revolutionary’ vaan ’evolutionary’ julkaisu. Tuleva Java 8 voi sitten olla se vallankumous.

Oraclen JRockit alkaa myös hivuttautumaan HotSpot koneen rinnalle ja sitä viedään pidemmälle tulevissa JDK 7 Update versioissa. Luin jostain, että kun virtuaalikoneet on saatu fuusioitua täysin yhteen, uusia piirteitä on tullut noin 15 prosenttia.. Suurin osahan niissä on hyvinkin samaa.

Edit:

No niin, saatiinhan se esitekin ulos Tieturin kurssista: Löytyy siis osoitteesta http://www.tieturi.fi/kurssit/kurssi.html?course=83904105&category=Open%2BSource%2BJava&training=04.11.2011 kaikille niille jotka ovat kiinnostuneita syleilemään uutta alustaa 😉

 

 

Joka tapauksessa, JDK 7 on ladattavissa vaikka tänään tutusta osoitteesta http://www.oracle.com/technetwork/java/javase/downloads/index.html – tosin lienee syytä antaa varoituksen sana, joidenkin Apachen open source pakettien kanssa on todettu bugeja JDK 7:ssä, jotka varmaan korjaillaan pikaisella päivityksellä. Joka tapauksessa, uunituore JDK 7 GA kannattaa testata rauhassa toimivuuden osalta.

http://blogs.oracle.com/henrik/entry/java_7_is_ga

http://www.eweekeurope.co.uk/news/apache-developers-java-7-contains-bugs-35619