I. Environnement de développement▲
Tout au long de ce tutoriel, nous utiliserons : Eclipse JUNO, jdk-7u13, Maven 3, JSF 2.1.4, Spring, Primefaces, PrettyFaces, Junit, Mockito.
II. Déclaration des bibliothèques via Maven▲
Commençons par créer un projet Maven et ajoutons toutes ses dépendances afin d'intégrer toutes les bibliothèques tierces nécessaires.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
126.
127.
128.
129.
130.
131.
132.
133.
134.
135.
136.
137.
138.
139.
140.
141.
142.
143.
144.
145.
146.
147.
148.
149.
150.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.paloit</groupId>
<artifactId>myApp</artifactId>
<packaging>war</packaging>
<version>1.0-SNAPSHOT</version>
<name>MyApp</name>
<description>MyApp</description>
<properties>
<!-- Build -->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.6</java.version>
<wtp.version>2.0</wtp.version>
<!-- Test -->
<junit.version>4.10</junit.version>
<!-- SPRING -->
<spring.version>3.2.0.RELEASE</spring.version>
<!-- HIBERNATE -->
<hibernate.version>4.1.9.Final</hibernate.version>
<!-- Servlet et pages -->
<mojarra.version>2.1.4</mojarra.version>
<jstl.version>1.2</jstl.version>
<servlet-api.version>2.5</servlet-api.version>
<!-- Plugins -->
<maven-compiler-plugin.version>2.3.2</maven-compiler-plugin.version>
<maven-eclipse-plugin.version>2.8</maven-eclipse-plugin.version>
<tomcat-maven-plugin.version>1.1</tomcat-maven-plugin.version>
</properties>
<dependencies>
<!-- JSF -->
<dependency>
<groupId>com.sun.faces</groupId>
<artifactId>jsf-api</artifactId>
<version>${mojarra.version}</version>
</dependency>
<dependency>
<groupId>com.sun.faces</groupId>
<artifactId>jsf-impl</artifactId>
<version>${mojarra.version}</version>
<scope>compile</scope>
</dependency>
<!-- PRETTY FACES -->
<dependency>
<groupId>com.ocpsoft</groupId>
<artifactId>prettyfaces-jsf2</artifactId>
<version>3.3.2</version>
</dependency>
<!-- JSTL -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>${jstl.version}</version>
</dependency>
<!-- Servlet 2.5 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>${servlet-api.version}</version>
<scope>provided</scope>
</dependency>
<!-- SPRING -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- HIBERNATE -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>${hibernate.version}</version>
</dependency>
<!-- JUNIT -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<version>2.2.9</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>4.2.0.CR1</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-c3p0</artifactId>
<version>4.2.0.CR1</version>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>20030203.000129</version>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.9.5</version>
</dependency>
</dependencies>
<build>
<finalName>myApp</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-eclipse-plugin</artifactId>
<version>${maven-eclipse-plugin.version}</version>
<configuration>
<wtpversion>${wtp.version}</wtpversion>
<source>${java.version}</source>
<target>${java.version}</target>
<downloadSources>true</downloadSources>
<downloadJavadocs>true</downloadJavadocs>
</configuration>
</plugin>
</plugins>
</build>
</project>
III. Mise en place de la couche entité▲
Avant tout, nous allons créer une entité Personne qui nous servira d'exemple tout au long de ce tutoriel.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
package com.paloit.entities;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.hibernate.annotations.GenericGenerator;
@Entity
@Table(name = "person")
public class Person implements Serializable {
/**
*
*/
private static final long serialVersionUID = 8496087166198616020L;
private String id;
private String name;
private Integer age;
@Id
@GeneratedValue(generator = "system-uuid")
@GenericGenerator(name = "system-uuid", strategy = "uuid")
@Column(name = "PersonId")
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
@Column(name = "PersonName", nullable = false)
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Column(name = "PersonAge")
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof Person == false) {
return false;
}
if (this == obj) {
return true;
}
final Person person = (Person) obj;
return new EqualsBuilder().append(name, person.getName())
.append(id, person.getId()).append(age, person.getAge())
.isEquals();
}
}
Comme vous le voyez, nous avons utilisé une approche de génération des identifiants (ID). Ainsi, lors de l'enregistrement, les identifiants seront automatiquement générés.
Une fois notre entité créée, nous passerons à la configuration de la connexion de la base de données. Ici, nous utiliserons une base de données mémoire (HSQL) et Spring database-embeded, qui est une base de données embarquée facile à configurer.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/jdbc
http://www.springframework.org/schema/jdbc/spring-jdbc-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<jdbc:embedded-database id="dataSource" type="HSQL">
<!-- <jdbc:script location="schema.sql"/> -->
<!-- <jdbc:script location="data.sql"/> -->
</jdbc:embedded-database>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"
p:dataSource-ref="dataSource" p:packagesToScan="com.paloit.entities">
<property name="hibernateProperties">
<value>
hibernate.format_sql=true
hibernate.dialect=org.hibernate.dialect.HSQLDialect
hibernate.hbm2ddl.auto=create
</value>
</property>
</bean>
<bean id="transactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory">
<ref bean="sessionFactory" />
</property>
</bean>
<tx:annotation-driven />
</beans>
IV. Mise en place de la couche DAO▲
Avant de passer à la partie DAO, faisons des tests unitaires afin de nous assurer que notre configuration est correcte. Créons la classe PersonTest.java :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
package com.paloit.dao;
import java.util.List;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.paloit.entities.Person;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({ "/db-config.xml" })
public class PersonTest {
static Session session;
@Autowired
public void setFactory(SessionFactory factory) {
session = factory.openSession();
}
@Test
public void testConfiguration() {
// Setup
Person person = new Person();
person.setName("Palo");
person.setAge(21);
session.beginTransaction();
// Action
person=(Person) session.merge(person);
List persons = session.createCriteria(Person.class).list();
// Test
Assert.assertEquals(1, persons.size());
Assert.assertEquals(person, persons.get(0));
}
@AfterClass
public static void tearDown(){
session.close();
}
}
Dans la plupart des tutoriels que j'ai consultés, les tests se font seulement au niveau des DAO. Je pense que cela ressemble plus à un test d'intégration qu'un test unitaire. En effet, dès lors qu'on considère que la partie mapping et la partie DAO sont deux couches différentes, on doit les tester l'une après l'autre afin de s'assurer qu'elles peuvent fonctionner séparément.
Une fois la partie mapping testée, nous allons maintenant créer la classe DAO. Cette classe sera très simple, car elle comporte seulement les méthodes CRUD (Create, Read, Update and Delete).
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
package com.paloit.dao;
import java.util.List;
import com.paloit.entities.Person;
public interface PersonDao {
void savePerson(Person person);
List getAllPersons();
Person getPersonById(String id);
void deletePerson(Person person);
List getPersonbyName(String name);
}
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
package com.paloit.dao;
import java.util.List;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import com.paloit.entities.Person;
@Repository("personDao")
public class PersonDaoImpl implements PersonDao{
private SessionFactory sessionFactory;
@Autowired
public void setSessionFactory(SessionFactory factory){
sessionFactory=factory;
}
public void savePerson(Person person) {
sessionFactory.getCurrentSession().merge(person);
}
@SuppressWarnings("unchecked")
public List getAllPersons() {
return sessionFactory.getCurrentSession().createCriteria(Person.class).list();
}
public Person getPersonById(String id) {
return (Person) sessionFactory.getCurrentSession().get(Person.class, id);
}
public void deletePerson(Person person) {
sessionFactory.getCurrentSession().delete(person);
}
public List getPersonbyName(String name) {
return sessionFactory.getCurrentSession().createQuery("From Person WHERE name =:name").list();
}
}
Passons maintenant au test du DAO. Pour cela, nous allons utiliser un framework de Mock pour simuler les appels de certains objets de la couche DAO. En effet, les objets utilisés sont l'un des problèmes majeurs des tests unitaires . Certains sont difficiles à instancier alors que d'autres ont un comportement difficilement contrôlable. C'est là qu'interviennent les frameworks de Mock. Ces frameworks permettent de simuler l'appel des méthodes et de les contrôler. Dans ce tutoriel, nous allons utiliser Mockito.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
package com.paloit.dao;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.List;
import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import com.paloit.entities.Person;
public class PersonDaoTest {
static PersonDaoImpl dao;
static SessionFactory factory;
static Session session;
@BeforeClass
public static void initClass(){
dao = new PersonDaoImpl();
factory = mock(SessionFactory.class);
session = mock(Session.class);
dao.setSessionFactory(factory);
}
@Before
public void init() {
reset(factory,session);
when(factory.getCurrentSession()).thenReturn(session);
}
@Test(expected = HibernateException.class)
public void testCreateDataKO() {
// Setup
Person user = new Person();
when(session.merge(user)).thenThrow(new HibernateException("fail"));
// Action
dao.savePerson(user);
}
@Test
public void testCreateDataOK() {
// Setup
Person input = new Person();
// Action
dao.savePerson(input);
// test
verify(factory, times(1)).getCurrentSession();
verify(session, times(1)).merge(input);
}
@Test(expected = HibernateException.class)
public void testRetrieveDataKO() {
// /Setup
Criteria criteria = mock(Criteria.class);
when(session.createCriteria(Person.class)).thenReturn(criteria);
when(criteria.list()).thenThrow(new HibernateException(""));
dao.getAllPersons();
}
@Test
public void testRetrieveDataOK() {
// /Setup
Criteria criteria = mock(Criteria.class);
when(session.createCriteria(Person.class)).thenReturn(criteria);
List persons = mock(List.class);
when(criteria.list()).thenReturn(persons);
// Action
dao.getAllPersons();
// Test
verify(session, times(1)).createCriteria(Person.class);
verify(criteria, times(1)).list();
}
@Test(expected = HibernateException.class)
public void testDeleteDataKO() {
// Setup
Person input = new Person();
doThrow(new HibernateException("")).when(session).delete(input);
// Action
dao.deletePerson(input);
}
@Test
public void testDeleteDataOK() {
// Setup
Person input = new Person();
// Action
dao.deletePerson(input);
// Test
verify(factory, times(1)).getCurrentSession();
verify(session, times(1)).delete(input);
}
}
Passons maintenant à la configuration Spring. Nous allons ainsi créer un fichier XML qu'on appellera applicationContext.xml. Dans ce fichier, nous ferons un scan des packages contenant les annotations et importerons le fichier db-config.xml.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<import resource="db-config.xml" />
<context:component-scan base-package="com.paloit" />
</beans>
V. Mise en place de la couche service▲
Nous allons créer les classes de la couche service. Celles-ci appellent la classe PersonDao. Nous allons l'injecter via Spring :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
package com.paloit.manager;
import java.util.List;
import com.paloit.entities.Person;
public interface PersonManager {
void savePerson(Person person);
List getAllPersons();
Person getPersonById(String id);
void deletePerson(Person person);
}
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
package com.paloit.manager;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import com.paloit.dao.PersonDao;
import com.paloit.entities.Person;
@Service
@Transactional(readOnly = true)
public class PersonManagerImpl implements PersonManager {
private PersonDao personDao;
@Autowired
public void setPersonDao(PersonDao personDao) {
this.personDao = personDao;
}
@Transactional(readOnly = false)
public void savePerson(Person person) {
personDao.savePerson(person);
}
public List getAllPersons() {
return personDao.getAllPersons();
}
public Person getPersonById(String id) {
return personDao.getPersonById(id);
}
@Transactional(readOnly = false)
public void deletePerson(Person person) {
personDao.deletePerson(person);
}
}
Maintenant, passons à la classe de Test. Comme dans la classe DAO, on utilisera Mockito.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
package com.paloit.manager;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import org.hibernate.HibernateException;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import com.paloit.dao.PersonDao;
import com.paloit.entities.Person;
public class PersonManagerImplTest {
private static PersonManagerImpl managerImpl;
private static PersonDao personDao;
@BeforeClass
public static void setUpBeforeClass() throws Exception {
managerImpl = new PersonManagerImpl();
personDao = mock(PersonDao.class);
managerImpl.setPersonDao(personDao);
}
@Before
public void setUp() throws Exception {
reset(personDao);
}
@Test(expected = HibernateException.class)
public void testSavePersonKO() {
// Setup
Person person = new Person();
doThrow(new HibernateException("fail")).when(personDao).savePerson(
person);
// Action
managerImpl.savePerson(person);
}
@Test
public void testSavePersonOK() {
// Setup
Person person = new Person();
// Action
managerImpl.savePerson(person);
// Test
verify(personDao, times(1)).savePerson(person);
}
@Test(expected = HibernateException.class)
public void testGetPersonByIdKO() {
// Setup
when(personDao.getPersonById("id")).thenThrow(
new HibernateException("fail"));
// Action
managerImpl.getPersonById("id");
}
@Test
public void testGetPersonByIdOK() {
// Setup
Person input = new Person();
String id = "id";
input.setAge(23);
input.setId(id);
input.setName("palo");
when(personDao.getPersonById(id)).thenReturn(input);
// Action
Person output = managerImpl.getPersonById(id);
// Test
assertEquals(input, output);
verify(personDao, times(1)).getPersonById(id);
}
}
VI. Conclusion▲
Je n'ai pas testé toutes les méthodes : je vous laisse le soin d'effectuer le test des autres ! Nous aborderons la suite dans la deuxième partie de notre tutoriel, soit la partie graphique avec JSF et Primefaces, ainsi que la gestion des URL avec Pretty Faces.
VII. Remerciements▲
Cet article a été publié avec l'aimable autorisation de la société PaloITPaloIT.
Nous tenons à remercier ClaudeLELOUP pour sa relecture orthographique attentive de cet article et Mickael Baron pour la mise au gabarit.







