Å trene på Java EE
For å bli bedre må man trene. For å bli bedre med avanserte ting, må man forstå de grunnleggende tingene bra. For å vite hvorfor man bruker avanserte verktøy, må man prøve å jobbe uten dem. Derfor har jeg de siste ukene trent mange ganger på å lage en veldig enkel webapplikasjon i Java. For hele applikasjonen har jeg startet med å skrive testene før koden som implementerer funksjonaliteten.
Dersom du vil prøve deg på samme øvelse, inneholder denne artikkelen litt informasjon for å komme i gang. Start med koden under og følg feilmeldingene. Send en kommentar dersom du ikke kommer videre fra en feilmelding, så får vi en FAQ.
Oppgaven
Løs et så enkelt som mulig problem som involverer websider og database med så enkel teknologi om mulig.
Oppgaven jeg har laget går ut på å opprette personer med fullt navn og søke etter personer basert på navnet deres. For å gjøre oppgaven så lite som mulig har jeg valgt å la personer kun ha ett informasjonsfelt: Fullt navn. Denne oppgaven tar cirka 2-3 timer uten øvelse og du kan få den ned i 60-90 minutter med trening.
Du kan naturligvis velge en annen oppgave, men uansett hva du velger: Det er mer lærerikt å gjenta den samme oppgaven flere ganger enn å utføre en avansert oppgave.
Når jeg utfører oppgaven er det viktigste jeg lærer meg å forstå feilmeldingene som guider meg gjennom utviklingen. Dersom du trenger hjelp til å komme til de første feilmeldingene kan du se resten av artikkelen.
Steg for steg: Startpunktet
Selv om jeg valgte veldig enkel teknologi for implementasjonen, har jeg valgt et større sett med biblioteker for å skrive testene. Jeg bruker følgende når jeg skriver testene:
- JUnit 4.6
- Jetty 6.1.22
- HSqlDb 1.8.0.10
- WebDriver-HtmlUnit 0.6.1039
- Mockito 1.8.0
- FEST-assert 1.2 (ikke påkrevd, men gjør testene søtere)
Den eneste teknologien jeg har valgt for implementasjonen er Servlet-API 2.5 og Hibernate-Annotations 3.4.0.GA. For at du skal slippe å plundre så mye med avhengigheter før du kommer i gang har jeg laget en pom.xml-fil som du kan ta utgangspunkt i.
Web-tester
For å starte utviklingen, er det lurt med en test som starter på utsiden av applikasjonen. Noe slikt:
- Start opp miljøet
- Legg inn en person
- Søk etter personen
Slik kommer du i gang med en test som går mot en web applikasjon:
int SERVER\_PICKS\_PORT = 0;
org.mortbay.jetty.Server server =
new org.mortbay.jetty.Server(SERVER\_PICKS\_PORT);
server.addHandler(
new org.mortbay.jetty.webapp.WebAppContext("src/main/webapp", "/"));
server.start();
int serverPort = server.getConnectors()[0].getLocalPort();
org.openqa.selenium.WebDriver browser =
new org.openqa.selenium.htmlunit.HtmlUnitDriver();
browser.get("http://localhost:" + serverPort + "/");
browser.findElement(By.linkText("Create person"));
Dette oppsettet forventer å finne web.xml
-fila på src/main/webapp/WEB-INF/web.xml
.
Funksjonell test
En funksjonell test definerer kravene i applikasjonen. Det er lurt å gjøre funksjonelle tester så raske som overhode mulig, samtidig som de går gjennom alle kravene. En funksjonell test trenger ikke være en ende-til-ende test, slik som eksempelet over. Dette er viktig, fordi ende-til-ende tester er ofte veldig trege. Her er noen eksempler på funksjonelle tester:
- Vis en siden for å opprette nye personer
- Opprett en ny person
- Verifiser at personens navn er oppgitt og ikke inneholder ulovlige tegn
- Vis en side for å søke etter personer
- Vis alle personer dersom søkestreng ikke er angitt
- Søk etter angitt søkestreng
En funksjonell test kan se slik ut:
PersonServlet servlet = new PersonServlet();
HttpServletRequest req =
org.mockito.Mockito.mock(HttpServletRequest.class);
HttpServletResponse resp =
org.mockito.Mockito.mock(HttpServletResponse.class);
PersonDao personDao =
org.mockito.Mockito.mock(PersonDao.class);
servlet.setPersonDao(personDao);
org.mockito.Mockito.when(req.getMethod())
.thenReturn("POST");
org.mockito.Mockito.when(req.getPathInfo())
.thenReturn("/create.html");
org.mockito.Mockito.when(req.getParameter("full\_name"))
.thenReturn("Johannes Brodwall");
StringWriter pageSource = new StringWriter();
org.mockito.Mockito.when(resp.getWriter())
.thenReturn(new PrintWriter(pageSource));
servlet.service(req, resp);
org.mockito.Mockito.verify(personDao)
.create(Person.byName("Johannes Brodwall"));
org.fest.assertions.Assertions.assertThat(pageSource.toString())
.contains("Personen er opprettet");
Data-aksess-test
Hibernate forenkler databasebruken mye. Men Hibernate er selv komplekst og når man bruker det på mer avanserte måter fortjener det egne tester. En typisk test med Hibernate kan være:
- Legg i tre personer i database
- Søk etter en del av navnet på en av dem
- Sjekk at du får tilbake akkurat den du forventet
Når jeg starter med Hibernate, lager jeg en test som dette, og følger feilmeldingene. Pass på å både følge feilmeldinger i loggen og stack tracer.
AnnotationConfiguration conf = new AnnotationConfiguration()
.setProperty(Environment.URL, "jdbc:hsqldb:mem:persondaotest");
PersonDao dao = new HibernatePersonDao(conf.buildSessionFactory());
dao.create(Person.withName("foo"));
org.fest.assertions.Assertions.assertThat(dao.find(null))
.containsExactly(Person.withName("foo"));
Følg feilmeldingene herfra.
Integrasjon
En veldig vanlig måte for web serveren å overlevere spesielt ting som DataSources til applikasjonen er via JNDI. I Jetty kan du gjøre dette i Web-testen på følgende måte:
org.hsqldb.jdbc.jdbcDataSource ds = new org.hsqldb.jdbc.jdbcDataSource();
ds.setDatabase("jdbc:hsqldb:mem:personwebtest");
ds.setUser("sa");
new org.mortbay.jetty.plus.naming.Resource("jdbc/primaryDs", ds);
// Oppstart av Jetty som vist over
Konklusjon
Å gjøre en liten øvelse som dette er en god måte å bli bevisst hvilke vaner du har og hvor lang tid det egentlig tar for deg å gjøre oppgavene dine. Du vil oppleve at det å skrive tester før koden føles som om det går saktere enn du tror du er vant til. Men dersom du er som meg, vil du også oppleve noe annet: Når du tester ut applikasjonen første gang (du kan gjøre dette med Jetty, naturligvis) så er sjansene gode for at den vil være nokså feilfri og at debugging i stor grad er overflødig. Jeg vet ikke med deg, men debugging er en aktivitet jeg gjerne blir kvitt.
Comments:
[Ornob_jlx] - Jul 13, 2010
My goal is for every member of the audience to leave with tangible down-to-earth ideas and skills they can start implementing before they exit the conference room. Taking action creates results. Tell me the perfect outcome for your audience, and I will create a custom presentation that exceeds your expectations.
http://www.globalspeakerforce.com/keynotespeake…
[heim] - Dec 2, 2009
Det er spennende å lese om erfaringene med hva vi kan kalle “avanserte” katas. Dette er bread and butter for Java EE-utviklere. Vanlige, enkle, ukompliserte katas hjelper utvikleren å bli bedre kjent med red-green-refactor-teknikken. På det avanserte nivået øver vi på de teknikkene vi bruker til daglig.
For utviklere som er nybegynnere innen TDD kan jeg ikke anbefale å starte med en så avansert kata som dette, men for den viderekommende så er det slike ting som dette som man bør øve på.
Jeg ser fram til det tidspunktet hvor en gruppe kan ta for seg en slik problemstilling på f.eks. Oslo Coding Dojo, da har vi kommet langt som profesjonelle.
[Oddbjorn Lona] - May 22, 2010
Om en noe sent, så komplimenterte denne videoen (/2010/05/02/six-idea…), eller er det omvendt. Skulle jeg kanskje hadde begynt med denne her først.
Takk for at du tar tiden til å skrive dette.