När ett test skrivs för en klass som har beroenden till en datakälla, en extern service eller bara en annan klass är mockning ofta väldigt användbart. Ibland kan detta leda till att produktionskod anpassas för att det ska gå att skriva dessa tester. Nedan är exempel på ett test som testar en service som använder en entity manager.
package se.cygni.blog;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import javax.persistence.EntityManager;
import org.junit.Before;
import org.junit.Test;
public class BlogServiceTest {
private BlogService service;
private EntityManager entityManager;
@Before
public void setup() {
entityManager = mock(EntityManager.class);
service = new BlogService(entityManager);
}
@Test
public void addEntry() {
Entry entry = new Entry("title", "text");
service.addEntry(entry);
verify(entityManager).persist(entry);
}
}
Testet i exemplet är relativt enkelt och verifierar endast att persist anropas i addEntry och ger oss följande kod för ett Spring-baserat projekt:
package se.cygni.blog;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class BlogService {
@PersistenceContext
private EntityManager entityManager;
public BlogService() {
}
protected BlogService(EntityManager entityManager) {
this.entityManager = entityManager;
}
@Transactional
public void addEntry(Entry entry) {
entityManager.persist(entry);
}
}
För att kunna använda en mockad entityManager har vi lagt till en konstruktor som tar en EntityManager som BlogService sedan kan använda. Använder vi dessutom ”field injection” är vi tvugna att lägga till en no-args-konstruktor. Alternativt hade vi kunnat lägga till en setter istället.
protected void setEntityManager(EntityManager entityManager) {
this.entityManager = entityManager;
}
Det är väl antagligen inte så illa att behöva lägga till konstruktorer alternativt setters även om det blir värre när en klass har flera beroenden. Däremot känns det ju lite onödigt när både Spring och Java EE numera stödjer field injection. Det går ju att köra enhetstesterna med till exempel Spring och låta Spring injicera alla beroenden. Detta anses dock inte helt lämpligt för enhetstester utan lämpar sig bättre för integrationstester. Istället kan Mockito ta hand om injicering, genom att köra testerna med MockitoJUnitRunner och använda annotationerna @Mock och @InjectMocks. Mockito fungerar då i princip som en enkel IOC-container och några extra konstruktorer eller setters behövs inte.
package se.cygni.blog;
import static org.mockito.Mockito.verify;
import javax.persistence.EntityManager;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public class BlogServiceTest {
@Mock
private EntityManager entityManager;
@InjectMocks
private BlogService service = new BlogService();
@Test
public void addEntry() {
Entry entry = new Entry("title", "text");
service.addEntry(entry);
verify(entityManager).persist(entry);
}
}
Nyligen läste jag ett intressant blogginlägg där Spring jämfördes med Java EE 6. Författaren drar slutsatsen att det numera är minst lika enkelt att utveckla en applikation för Java EE-plattformen som för Spring. Då Spring, Spring MVC inkluderat, är väldigt populärt blev jag nyfiken på hur detta kan integreras med Java EE för att lättare dra nytta av allt som en Java EE-container kan erbjuda. Det visade sig att detta var förbluffande enkelt, till exempel går det utan ytterligare Spring-konfiguration direkt injicera en Session Bean i en Spring-controller.
@Controller
public class MyController {
@EJB(mappedName="java:module/MyService")
private MyService myService;
@RequestMapping(value = "/", method = RequestMethod.GET)
public String home(Model model) {
model.addAttribute("message", myService.getMessage());
return "home";
}
}
Nyckeln till att injektionen med @EJB-annotation fungerar, i exemplet ovan, är att mappedName används. Spring utför då en JNDI-lookup på det angivna JNDI-namnet. Eftersom JNDI har standardiserats i Java EE 6 är dessutom namnen portabla mellan applikationsservrar. Stödet för Java EE 6 kommer säkerligen förbättras i framtida versioner av Spring så det finns bra defaults för ejb-lookups med JNDI.
En annan spännande detalj med detta exempel är att MyService inte behöver vara ett interface eftersom Java EE 6 inte kräver detta för lokala session beans. Dessutom kan session beans paketeras tillsammans med resten av webbapplikationen i war-arkivet. D.v.s. det behövs ingen ejb-jar och inget EAR-arkiv.
Läs hela artikeln på Java Code Geeks.
Java Bean Validation (JSR 303) definierar en meta-data-modell och ett API för validering av klasser. Implementationer av denna specifikation gör det möjligt att definiera och utföra validering med hjälp av annotationer eller xml. Exemplet nedan illusterar detta för ett användarnamn:
@NotNull
@Size(min = 5, max = 20)
@Pattern(regexp = "[a-zA-Z0-9\\._-]+")
private String userName;
Bean Validation är inte knutet till något speciellt ramverk eller lager. Det finns däremot bra stöd för Bean Validation i flertalet populära ramverk, såsom Hibernate och JSF 2. Dessutom är det enkelt att utföra validering via API:t:
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
...
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
Validator validator = factory.getValidator();
Set<ConstraintViolation<Customer>> constraintViolations = validator.validate(customer);
För att komma igång med ett Bean Validation (Hibernate Validator 4.x är referensimplementationen) i ett Maven-projekt lägg till följande beroenden i din pom:
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>4.2.0.Final</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jdk14</artifactId>
<version>1.6.1</version>
</dependency>
Används Hibernate eller JSF 2 räcker det oftast med att lägga till ovanstående beroenden så länge de finns tillgängliga i applikationens class path. Används JSF 2 går det till exempel att aktivera validering för ett inmatningsfält på följande sätt:
<h:inputText value="#{customer.age}">
<f:validateBean/>
</h:inputText>
Bean validation stödjer dessutom validering av grupper som gör det möjligt att validera olika aspekter av ett objekt:
@Min(value=15, groups=AgeCheck.class)
private int age;
Denna artikel ingår i serien Spring från början och kommer behandla det springstöd som finns batchjobb.
Läs mer >>
- cygni
- 19 september 2011
- Kommentering avstängd
Cygni är ett konsultbolag med ambitionen att vara Stockholms bästa arbetsgivare för skickliga systemutvecklare och systemarkitekter. Bland våra kunder finns Skandia, Aftonbladet, Cinnober, LensWay, Eniro, Metro, Com Hem och många andra företag som är ledande inom sin bransch. Cygni har således ingen branschinriktning, vi är specialister på agil systemutveckling och den kompetensen applicerar vi framgångsrikt i många olika branscher.
Cygni består idag av 40 killar och tjejer i åldrarna 25-57. Vi upplever en mycket stark efterfrågan på våra tjänster och söker därför nu 5 stycken seniora javautvecklare/systemarkitekter som trivs i konsultrollen.
De flesta på Cygni är civilingenjörer men det är inget krav. Däremot vill vi att du älskar systemutveckling och har minst fyra års javaerfarenhet. Du ska helst ha jobbat med agil metodik och moderna ramverk som Spring och Hibernate. Som konsult hos oss arbetar du ofta tillsammans med Cygni-kollegor i utmanande och utvecklande uppdrag hos någon av våra kunder i centrala Stockholm. Ett intresse för exempelvis Scala, Groovy och Ruby är meriterande.
Läs gärna mer om oss på cygni.se eller på vår teknikblogg Stacktrace!
Jersey 1.7 kommer med stöd för Spring 2.5, men projektet jag jobbar med för tillfället använder Spring 3.0. I denna artikel tänkte jag beskriva vad jag gjorde för att få dessa att funka tillsammans med Maven.
Läs mer >>
Detta inlägg visar hur Dependency Injection (DI) kan användas med hjälp av CDI, Guice och Spring. Depencency Injection beskrivs bland annat i Robert Buréns artikel Spring som DI-ramverk. Exemplet i detta inlägg är kraftigt influerat av en artikel jag läste på DZone.
Spring och Guice är två av de vanligaste ramverken för DI. Dessa ramverk kan användas i standalone-, web- och enterprise-applikationer. CDI är ett standardiserat sätt för DI som kommer med Java EE 6 och kan tills vidare därför endast användas i en enterprise-miljö. Spring erbjuder dock stöd för några av de features som definieras av CDI.
Läs mer >>
Apache Camel är ett javabaserat integrationsramverk som innehåller en mängd komponenter. När man konfigurerar kan man använda Spring xml, annoteringar och en Java DSL. Allt är väl beskrivet på Camels hemsida. Eftersom Scala är utvecklat för att enkelt kunna integrera med Java och Javas ramverk är det inte konstigt att är väldigt enkelt att integrera Scala-komponenter i Camel. Det finns även en Scala DSL som kan användas för att konfigurera Camel-routar med. Jag har skrivit ett litet Scala-Camel projekt, WeirdTranslator för att visa på hur Scala i Camel kan fungera. WeirdTranslator är en variant på viskleken, man tar en mening och översätter den mellan ett antal språk och avslutar med att översätta till ursprungsspråket. I detta fallet finns det två vägar att få in och ut text antingen via GTalk, XMPP, och direkt med en TCP socket.
Läs mer >>
Tidigare beskrev Tommy Wassgren hur man kan använda profiles i Spring 3.1 för att definiera olika kör-profiler. T.ex. en profil för utveckling och en för produktion, vilka man enkelt kan växla mellan m.h.a. en jvm parameter. Kruxet är dock att 3.1 inte är släppt, så i väntan på detta vad gör man då om sitter i t.ex. Spring 2.5?
Läs mer >>
Chris Beams bloggar om nya features från milestone-release nummer ett av kommande Spring Framework 3.1. I den första artikeln visar han ”Bean Definition Profiles”. Ett användningsområde för profiler är att kunna scopa in och ut komponenter beroende på om du exekverar din applikation i utvecklingsmiljön eller i produktionsmiljön. Han exemplifierar med klassikern att använda olika datakällor för utveckling och produktion:
Läs mer >>