SlideShare une entreprise Scribd logo
1  sur  25
Tester unitairement
une application Java
11 février 2016
Objectifs
 Positionnement des TU par rapport aux autres catégories de tests
 Tour d’horizon des outils facilitant l’écriture de tests
 Zoom sur Mockito
 Exemples concrets
Sommaire
 Les différents types de tests automatisés
 Objectifs des tests unitaires
 Stratégies de mise en œuvre des tests unitaires
 Bonnes pratiques & Difficultés
 Décomposition d’un test
 L’intérêt des mocks
 Boîte à outils : Unitils, Mockito, DbUnit et Spring Test
 Exemples de tests de DAO et de contrôleurs Spring MVC
 Tester du code legacy
 Tests unitaires
 Teste une méthode d’une classe en isolation du reste de l’application
 Les tests exécutés lors d’un mvn test
 Doivent fonctionner sur un poste de dév déconnecté du réseau
 Outils : JUnit, Mockito
 Tests d’intégration
 Teste un ensemble de composants
 Exemples :
 Web Service SOAP ou REST
 Service métier s’appuyant sur un DAO Hibernate interrogeant une base Oracle
 Outils : JUnit, SoapUI
Les différents types de tests automatisés (1/2)
 Tests fonctionnels
 Exécute des scénarios fonctionnels en simulant des interactions utilisateurs
 Outils : Selenium, HP UFT, CasperJS, Cucumber
 Tests de performance
 Simule la charge utilisateur sur un environnement iso-prod
 Outils : JMeter, Gatling, Dynatrace
 Tests de vulnérabilité, de robustesse aux pannes, d’installation, de
déploiement …
Les différents types de tests automatisés (2/2)
 Vérifier le comportement d’une fonctionnalité au
regard des exigences
 Tester ses développements
 Fait partie du job d’un développeur
 Sérénité vis-à-vis des tests d’intégration
 Se prémunir de régression lors de :
 Correction d’anomalies
 Evolutions fonctionnelles
 Montée de socle technique
 Refactoring
Objectifs des tests unitaires (1/2)
 Documenter le code
 Contribuent
 au design logiciel
 à la qualité générale de l’application
Objectifs des tests unitaires (2/2)
 Tester en priorité :
 le code complexe
 les corrections de bugs
 le code sensible (souvent amené à changer)
 Tester les cas limites
 Tests en boîte noire / boîte blanche
Stratégies de mise en œuvre des tests unitaires
 Valider le fonctionnement par des assertions
 Automatiser l’exécution des TU
 Un TU doit être rapide à exécuter
 Essayer d’avoir une méthode de test par scénario de test
 L’échec d’un test unitaire doit être compréhensible
 Importance du nom de la méthode de test unitaire
Bonnes pratiques
 Jeux de données
 Demande une connaissance fonctionnelle
 Peut-être complexe et fastidieux à initier
 Tester la couche d’accès aux données
 Code existant éloigné des principes inspirés de Clean Code
Difficultés
 Un test se décompose généralement en 3 étapes
import static org.junit.Assert.assertEquals;
import org.junit.Test;
public class CalculatorTest {
@Test
public void evaluateAdditionExpression() {
Calculator calculator = new Calculator();
Expression exp = new ArithmeticExpression("1+2+3");
int sum = calculator.evaluate(exp);
assertEquals(6, sum);
}
}
Décomposition d’un test
3. Then : vérifications du résultat
2. When : appel de la méthode testée
1. Given : initialise l’état du système
 Besoin : tester individuellement un service métier,
c’est à dire sans ses adhérences
L’intérêt des mocks
Service
Dao
Service
Dao
Mock
Code de production Configuration de test
à programmer
 ReflectionAssert.assertReflectionEquals de unitils-core
Boîte à outils : assertReflectionEquals de Unitils
User user1 = new User(1, "John", "Doe");
User user2 = new User(1, "John", "Connor");
assertReflectionEquals(user1, user2);
junit.framework.AssertionFailedError:
Expected: User<id=1, firstname="John", lastname="Doe">
Actual: User<id=1, firstname="John", lastname="Connor">
--- Found following differences ---
lastname: expected: "Doe", actual: "Connor"
Boîte à outils : Spring Test
 Conteneur léger accessible aux tests unitaires et d’intégration
 Support de JUnit 4 et TestNG
 Chargement du contexte Spring
 Injection de dépendances
 Mise en cache du contexte Spring
 Extensions de JUnit par
 Runner : SpringJUnit4ClassRunner
 Annotations : @ContextConfiguration, @Rollback, @Sql, @Repeat, @ActiveProfiles …
 Listeners : DependencyInjectionTestExecutionListener
 Bouchons prêts à l’emploi : MockHttpSession, MockHttpServletRequest …
 Classes utilitaires : JdbcTestUtils, AopTestUtils, ReflectionTestUtils,
TestTransaction …
 Spring MVC Test Framework
 Permet de charger en base des jeux de données
 A partir de fichier XML
 Favoriser des dataset les plus petits possibles
 Suite à un test, permet de vérifier l’état de la base
 Comparaison de l’état de la base avec un fichier XML
 Ce que DbUnit ne fait pas :
 Création de la base et du schéma
 Gestion des connexions et des transactions
 L’élaboration de jeux de données
Boîte à outils : DbUnit
 Créer un mock
Boîte à outils : Mockito (1/4)
import static org.mockito.Mockito.*;
List mockedList = mock(List.class);
assertNull(mockedList.get(0)); Les méthodes d’un mock non programmé
ne font rien. Elles retournent null ou false .
 Programmer un mock
LinkedList mockedList = mock(LinkedList.class);
when(mockedList.get(0)).thenReturn("first");
assertEquals("first", mockedList.get(0));
assertNull(mockedList.get(1));
Mockito permet de mocker aussi bien
des interfaces que des classes.
Simule un comportement
 Vérifier les interactions avec le mock
Boîte à outils : Mockito (2/4)
@Test
public void testAdminAuthentication() {
UserDao userDao = mock(UserDao.class);
UserService userService = new UserService(userDao);
User admin = new User("admin");
when(userDao.findOne("admin")).thenReturn(admin);
User loadedUser = userService.loadUserByUsername("admin");
verify(userDao).findOne("admin");
}
Mock le DAO
Programme le DAO
Vérifie l’interaction
 A utiliser judicieusement
 Lorsque la méthode testée ne renvoie pas de résultat
 Pour des problématiques de performance
 Partial mocking avec spy
 Enregistre les interactions
 Simule le comportement de méthodes choisies
Boîte à outils : Mockito (3/4)
List<String> list = new ArrayList<String>();
List<String> spyList = Mockito.spy(list);
spyList.add("one");
assertEquals(1, spyList.size());
Mockito.verify(spyList).add("one");
when(spyList.size()).doReturn(100);
assertEquals(100, spyList.size());
 ArgumentCaptor
Permet de récupérer la valeur des
paramètres d’appel d’un mock
Boîte à outils : Mockito (4/4)
public class Person {
private final String name;
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void inviteToParty(Person friend,
Party party) {
party.addGuest(friend);
}
}
ArgumentCaptor<Person> argument =
ArgumentCaptor.forClass(Person.class);
Party party = mock(Party.class);
Person john = new Person("John");
Person peter = new Person("Peter");
// Peter invites John to the party
peter.inviteToParty(john, party);
verify(party).addGuest(argument.capture());
// verify John was invited
assertEquals("John", argument.getValue().getName());
 Tester unitairement des DAO nécessite une base de données embarquée
 Les puristes les considèrent comme des tests d’intégration
 Spring Test
 Facilite le chargement de la configuration Spring liée à l’infrastructure
 Prend en charge la création du schéma
 Support des transactions
 Laisse la base inchangée après l’exécution du test (débrayable)
 Gestion manuelle des transactions
 Possibilité d’exécuter du code en dehors d’une transaction
Tester des DAO (1/3)
Tester des DAO (2/3)
 Avec DbUnit
accounts-dataset.xml
<?xml version='1.0' encoding='UTF-8'?>
<dataset>
<ACCOUNT ID="1" BIC="FR7030002005500000157845Z02" LABEL="Account 1"/>
<ACCOUNT ID="2" BIC="FR70300023455000021Z4234Y45" LABEL="Account 2"/>
</dataset>
public class TestHibernateAccountDao extend AbstractDaoTest<HibernateAccountDao> {
@Test
public void findAccountByIBan() {
DbUnitLoader.loadDataset("accounts-dataset.xml");
String iban = "FR70 3000 2005 5000 0015 7845 Z02";
Account account = dao.findAccountByIBan(iban);
assertNotNull(account);
assertEquals("Account 1", account.getDescription());
}
}
 Avec Spring Test
Tester des DAO (3/3)
Extrait du fichier spring/dao-config.xml
<beans profile="test">
<jdbc:embedded-database id="dataSource" type="HSQL">
<jdbc:script location="classpath:create-schema.sql"/>
</jdbc:embedded-database>
</beans>
<beans profile="production">
<jee:jndi-lookup id="dataSource"
jndi-name="jdbc/MyDataSource"/>
</beans>
<bean id="sessionFactory"
class="o.s.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref=" dataSource " />
…
</bean>
<bean id="transactionManager"
class="o.s.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<context:component-scan base-package="com.myapp.dao"/>
@ContextConfiguration(locations = {"classpath:spring/dao-config.xml"})
@RunWith(SpringJUnit4ClassRunner.class)
@ActiveProfiles("test")
public class ClinicDaoTests {
@Autowired ClinicDao clinicDao;
}
@Test @Transactional
public void shouldInsertOwner() {
Collection<Owner> owners = clinicDao.findOwnerByLastName("Schultz");
int found = owners.size();
Owner owner = new Owner();
owner.setFirstName("Sam");
owner.setLastName("Schultz");
owner.setAddress("4, Evans Street");
owner.setCity("Wollongong");
clinicDao.saveOwner(owner);
assertThat(owner.getId().longValue()).isNotEqualTo(0);
owners = clinicDao.findOwnerByLastName("Schultz");
assertThat(owners.size()).isEqualTo(found + 1);
}
 Avec Spring Test
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"classpath:spring/mvc-core-config.xml"})
@WebAppConfiguration
public class PetControllerTests {
@Autowired PetController petController;
MockMvc mockMvc;
@Before
public void setup() {
mockMvc = MockMvcBuilders.standaloneSetup(petController) .build();
}
@Test
public void testProcessUpdateFormSuccess() throws Exception {
mockMvc.perform(post("/owners/{ownerId}/pets/{petId}/edit", 1,1)
.param("name", "Betty")
.param("type", "hamster")
.param("birthDate", "2015/02/12")
)
.andExpect(status().is3xxRedirection())
.andExpect(view().name("redirect:/owners/{ownerId}"));
} }
Tester un contrôleur Spring MVC
Tester du code legacy
 Code faisant appel à un Singleton avec getIntance()
 Création d’un setter de visibilité package permettant de passer un
mock
 Méthode phagocyte
 Refactoring en sous méthodes qui seront testées individuellement
 Méthode private
 Changement de la visibilité en portée package
Conclusion
 Ecrire des tests, cela s’apprend
 Tester unitairement, c’est coder
 A chaque couche / techno, sa typologie de test

Contenu connexe

Tendances

Mocking in Java with Mockito
Mocking in Java with MockitoMocking in Java with Mockito
Mocking in Java with Mockito
Richard Paul
 
Presentation JEE et son écossystéme
Presentation JEE et son écossystémePresentation JEE et son écossystéme
Presentation JEE et son écossystéme
Algeria JUG
 

Tendances (20)

Cours JavaScript
Cours JavaScriptCours JavaScript
Cours JavaScript
 
Test unitaires
Test unitairesTest unitaires
Test unitaires
 
Mocking in Java with Mockito
Mocking in Java with MockitoMocking in Java with Mockito
Mocking in Java with Mockito
 
Test logiciel
Test logicielTest logiciel
Test logiciel
 
Java version 11 - les 9 nouveautes
Java version 11 -  les 9 nouveautesJava version 11 -  les 9 nouveautes
Java version 11 - les 9 nouveautes
 
Formation Spring Avancé gratuite par Ippon 2014
Formation Spring Avancé gratuite par Ippon 2014Formation Spring Avancé gratuite par Ippon 2014
Formation Spring Avancé gratuite par Ippon 2014
 
Spring Boot RestApi.pptx
Spring Boot RestApi.pptxSpring Boot RestApi.pptx
Spring Boot RestApi.pptx
 
Spring Boot & Containers - Do's & Don'ts
Spring Boot & Containers - Do's & Don'tsSpring Boot & Containers - Do's & Don'ts
Spring Boot & Containers - Do's & Don'ts
 
Interface fonctionnelle, Lambda expression, méthode par défaut, référence de...
Interface fonctionnelle, Lambda expression, méthode par défaut,  référence de...Interface fonctionnelle, Lambda expression, méthode par défaut,  référence de...
Interface fonctionnelle, Lambda expression, méthode par défaut, référence de...
 
Les Streams de Java 8
Les Streams de Java 8Les Streams de Java 8
Les Streams de Java 8
 
Presentation JEE et son écossystéme
Presentation JEE et son écossystémePresentation JEE et son écossystéme
Presentation JEE et son écossystéme
 
Les dessous du framework spring
Les dessous du framework springLes dessous du framework spring
Les dessous du framework spring
 
Whitebox testing of Spring Boot applications
Whitebox testing of Spring Boot applicationsWhitebox testing of Spring Boot applications
Whitebox testing of Spring Boot applications
 
Presentation of framework Angular
Presentation of framework AngularPresentation of framework Angular
Presentation of framework Angular
 
Chapitre 6 traitement des exceptions
Chapitre 6  traitement des exceptionsChapitre 6  traitement des exceptions
Chapitre 6 traitement des exceptions
 
Java cours n° 2 - classe-objet-constructeur
Java   cours n° 2 - classe-objet-constructeurJava   cours n° 2 - classe-objet-constructeur
Java cours n° 2 - classe-objet-constructeur
 
Bbl sur les tests
Bbl sur les testsBbl sur les tests
Bbl sur les tests
 
Java
JavaJava
Java
 
Test unitaire
Test unitaireTest unitaire
Test unitaire
 
Tests Logiciel
Tests LogicielTests Logiciel
Tests Logiciel
 

En vedette

Learned optimism
Learned optimismLearned optimism
Learned optimism
Andrew Chaa
 
pessimistic-development-presentation
pessimistic-development-presentationpessimistic-development-presentation
pessimistic-development-presentation
Jonathan Marbutt
 

En vedette (10)

Microservices with .Net - NDC Sydney, 2016
Microservices with .Net - NDC Sydney, 2016Microservices with .Net - NDC Sydney, 2016
Microservices with .Net - NDC Sydney, 2016
 
Learned optimism
Learned optimismLearned optimism
Learned optimism
 
Introduction à Angular JS
Introduction à Angular JSIntroduction à Angular JS
Introduction à Angular JS
 
Workshop Spring - Session 5 - Spring Integration
Workshop Spring - Session 5 - Spring IntegrationWorkshop Spring - Session 5 - Spring Integration
Workshop Spring - Session 5 - Spring Integration
 
Retours Devoxx France 2016
Retours Devoxx France 2016Retours Devoxx France 2016
Retours Devoxx France 2016
 
pessimistic-development-presentation
pessimistic-development-presentationpessimistic-development-presentation
pessimistic-development-presentation
 
Ces outils qui vous font gagner du temps
Ces outils qui vous font gagner du tempsCes outils qui vous font gagner du temps
Ces outils qui vous font gagner du temps
 
Workshop Spring 3 - Tests et techniques avancées du conteneur Spring
Workshop Spring  3 - Tests et techniques avancées du conteneur SpringWorkshop Spring  3 - Tests et techniques avancées du conteneur Spring
Workshop Spring 3 - Tests et techniques avancées du conteneur Spring
 
Spring Framework Petclinic sample application
Spring Framework Petclinic sample applicationSpring Framework Petclinic sample application
Spring Framework Petclinic sample application
 
Quoi de neuf à Devoxx France 2017 ?
Quoi de neuf à Devoxx France 2017 ?Quoi de neuf à Devoxx France 2017 ?
Quoi de neuf à Devoxx France 2017 ?
 

Similaire à Tester unitairement une application java

Qualité logicielle
Qualité logicielleQualité logicielle
Qualité logicielle
cyrilgandon
 
201303 - Java8
201303 - Java8201303 - Java8
201303 - Java8
lyonjug
 
PréSentation Qualoo Le Generateur De Code Java J2 Ee
PréSentation Qualoo   Le Generateur De Code Java J2 EePréSentation Qualoo   Le Generateur De Code Java J2 Ee
PréSentation Qualoo Le Generateur De Code Java J2 Ee
ST informatique services
 
Intro sur les tests unitaires
Intro sur les tests unitairesIntro sur les tests unitaires
Intro sur les tests unitaires
PHPPRO
 

Similaire à Tester unitairement une application java (20)

J Unit
J UnitJ Unit
J Unit
 
Comment écrire du code testable ?
Comment écrire du code testable ?Comment écrire du code testable ?
Comment écrire du code testable ?
 
testUnitaire (1).pptx
testUnitaire (1).pptxtestUnitaire (1).pptx
testUnitaire (1).pptx
 
Automatisation des tests - objectifs et concepts - partie 2
Automatisation des tests  - objectifs et concepts - partie 2Automatisation des tests  - objectifs et concepts - partie 2
Automatisation des tests - objectifs et concepts - partie 2
 
Les tests en PHP
Les tests en PHPLes tests en PHP
Les tests en PHP
 
Java 9 modulo les modules devoxx fr 2017
Java 9 modulo les modules devoxx fr 2017Java 9 modulo les modules devoxx fr 2017
Java 9 modulo les modules devoxx fr 2017
 
Présentation nouveauté java7
Présentation nouveauté java7Présentation nouveauté java7
Présentation nouveauté java7
 
Qualité logicielle
Qualité logicielleQualité logicielle
Qualité logicielle
 
Qualité de code et bonnes pratiques
Qualité de code et bonnes pratiquesQualité de code et bonnes pratiques
Qualité de code et bonnes pratiques
 
Xdt Tests Driven Architecture Process V1.0
Xdt Tests Driven Architecture Process V1.0Xdt Tests Driven Architecture Process V1.0
Xdt Tests Driven Architecture Process V1.0
 
La Tooling API, est-ce pour moi ? Bien sûr, viens voir pourquoi !
La Tooling API, est-ce pour moi ? Bien sûr, viens voir pourquoi !La Tooling API, est-ce pour moi ? Bien sûr, viens voir pourquoi !
La Tooling API, est-ce pour moi ? Bien sûr, viens voir pourquoi !
 
201303 - Java8
201303 - Java8201303 - Java8
201303 - Java8
 
RefCard Tests sur tous les fronts
RefCard Tests sur tous les frontsRefCard Tests sur tous les fronts
RefCard Tests sur tous les fronts
 
Tester avant de déployer ; comment tester ses déploiements ARM.
Tester avant de déployer ; comment tester ses déploiements ARM.Tester avant de déployer ; comment tester ses déploiements ARM.
Tester avant de déployer ; comment tester ses déploiements ARM.
 
05 visual basic .net - variables, procedures, arguments et structures de cont...
05 visual basic .net - variables, procedures, arguments et structures de cont...05 visual basic .net - variables, procedures, arguments et structures de cont...
05 visual basic .net - variables, procedures, arguments et structures de cont...
 
PréSentation Qualoo Le Generateur De Code Java J2 Ee
PréSentation Qualoo   Le Generateur De Code Java J2 EePréSentation Qualoo   Le Generateur De Code Java J2 Ee
PréSentation Qualoo Le Generateur De Code Java J2 Ee
 
Intro sur les tests unitaires
Intro sur les tests unitairesIntro sur les tests unitaires
Intro sur les tests unitaires
 
Atoum, le framework de tests unitaires pour PHP 5.3 simple, moderne et intuit...
Atoum, le framework de tests unitaires pour PHP 5.3 simple, moderne et intuit...Atoum, le framework de tests unitaires pour PHP 5.3 simple, moderne et intuit...
Atoum, le framework de tests unitaires pour PHP 5.3 simple, moderne et intuit...
 
[PFE] Master en ingénierie du logiciel
[PFE] Master en ingénierie du logiciel[PFE] Master en ingénierie du logiciel
[PFE] Master en ingénierie du logiciel
 
Play : Premiers pas par l'exemple le 120613
Play : Premiers pas par l'exemple le 120613Play : Premiers pas par l'exemple le 120613
Play : Premiers pas par l'exemple le 120613
 

Tester unitairement une application java

  • 1. Tester unitairement une application Java 11 février 2016
  • 2. Objectifs  Positionnement des TU par rapport aux autres catégories de tests  Tour d’horizon des outils facilitant l’écriture de tests  Zoom sur Mockito  Exemples concrets
  • 3. Sommaire  Les différents types de tests automatisés  Objectifs des tests unitaires  Stratégies de mise en œuvre des tests unitaires  Bonnes pratiques & Difficultés  Décomposition d’un test  L’intérêt des mocks  Boîte à outils : Unitils, Mockito, DbUnit et Spring Test  Exemples de tests de DAO et de contrôleurs Spring MVC  Tester du code legacy
  • 4.  Tests unitaires  Teste une méthode d’une classe en isolation du reste de l’application  Les tests exécutés lors d’un mvn test  Doivent fonctionner sur un poste de dév déconnecté du réseau  Outils : JUnit, Mockito  Tests d’intégration  Teste un ensemble de composants  Exemples :  Web Service SOAP ou REST  Service métier s’appuyant sur un DAO Hibernate interrogeant une base Oracle  Outils : JUnit, SoapUI Les différents types de tests automatisés (1/2)
  • 5.  Tests fonctionnels  Exécute des scénarios fonctionnels en simulant des interactions utilisateurs  Outils : Selenium, HP UFT, CasperJS, Cucumber  Tests de performance  Simule la charge utilisateur sur un environnement iso-prod  Outils : JMeter, Gatling, Dynatrace  Tests de vulnérabilité, de robustesse aux pannes, d’installation, de déploiement … Les différents types de tests automatisés (2/2)
  • 6.  Vérifier le comportement d’une fonctionnalité au regard des exigences  Tester ses développements  Fait partie du job d’un développeur  Sérénité vis-à-vis des tests d’intégration  Se prémunir de régression lors de :  Correction d’anomalies  Evolutions fonctionnelles  Montée de socle technique  Refactoring Objectifs des tests unitaires (1/2)
  • 7.  Documenter le code  Contribuent  au design logiciel  à la qualité générale de l’application Objectifs des tests unitaires (2/2)
  • 8.  Tester en priorité :  le code complexe  les corrections de bugs  le code sensible (souvent amené à changer)  Tester les cas limites  Tests en boîte noire / boîte blanche Stratégies de mise en œuvre des tests unitaires
  • 9.  Valider le fonctionnement par des assertions  Automatiser l’exécution des TU  Un TU doit être rapide à exécuter  Essayer d’avoir une méthode de test par scénario de test  L’échec d’un test unitaire doit être compréhensible  Importance du nom de la méthode de test unitaire Bonnes pratiques
  • 10.  Jeux de données  Demande une connaissance fonctionnelle  Peut-être complexe et fastidieux à initier  Tester la couche d’accès aux données  Code existant éloigné des principes inspirés de Clean Code Difficultés
  • 11.  Un test se décompose généralement en 3 étapes import static org.junit.Assert.assertEquals; import org.junit.Test; public class CalculatorTest { @Test public void evaluateAdditionExpression() { Calculator calculator = new Calculator(); Expression exp = new ArithmeticExpression("1+2+3"); int sum = calculator.evaluate(exp); assertEquals(6, sum); } } Décomposition d’un test 3. Then : vérifications du résultat 2. When : appel de la méthode testée 1. Given : initialise l’état du système
  • 12.  Besoin : tester individuellement un service métier, c’est à dire sans ses adhérences L’intérêt des mocks Service Dao Service Dao Mock Code de production Configuration de test à programmer
  • 13.  ReflectionAssert.assertReflectionEquals de unitils-core Boîte à outils : assertReflectionEquals de Unitils User user1 = new User(1, "John", "Doe"); User user2 = new User(1, "John", "Connor"); assertReflectionEquals(user1, user2); junit.framework.AssertionFailedError: Expected: User<id=1, firstname="John", lastname="Doe"> Actual: User<id=1, firstname="John", lastname="Connor"> --- Found following differences --- lastname: expected: "Doe", actual: "Connor"
  • 14. Boîte à outils : Spring Test  Conteneur léger accessible aux tests unitaires et d’intégration  Support de JUnit 4 et TestNG  Chargement du contexte Spring  Injection de dépendances  Mise en cache du contexte Spring  Extensions de JUnit par  Runner : SpringJUnit4ClassRunner  Annotations : @ContextConfiguration, @Rollback, @Sql, @Repeat, @ActiveProfiles …  Listeners : DependencyInjectionTestExecutionListener  Bouchons prêts à l’emploi : MockHttpSession, MockHttpServletRequest …  Classes utilitaires : JdbcTestUtils, AopTestUtils, ReflectionTestUtils, TestTransaction …  Spring MVC Test Framework
  • 15.  Permet de charger en base des jeux de données  A partir de fichier XML  Favoriser des dataset les plus petits possibles  Suite à un test, permet de vérifier l’état de la base  Comparaison de l’état de la base avec un fichier XML  Ce que DbUnit ne fait pas :  Création de la base et du schéma  Gestion des connexions et des transactions  L’élaboration de jeux de données Boîte à outils : DbUnit
  • 16.  Créer un mock Boîte à outils : Mockito (1/4) import static org.mockito.Mockito.*; List mockedList = mock(List.class); assertNull(mockedList.get(0)); Les méthodes d’un mock non programmé ne font rien. Elles retournent null ou false .  Programmer un mock LinkedList mockedList = mock(LinkedList.class); when(mockedList.get(0)).thenReturn("first"); assertEquals("first", mockedList.get(0)); assertNull(mockedList.get(1)); Mockito permet de mocker aussi bien des interfaces que des classes. Simule un comportement
  • 17.  Vérifier les interactions avec le mock Boîte à outils : Mockito (2/4) @Test public void testAdminAuthentication() { UserDao userDao = mock(UserDao.class); UserService userService = new UserService(userDao); User admin = new User("admin"); when(userDao.findOne("admin")).thenReturn(admin); User loadedUser = userService.loadUserByUsername("admin"); verify(userDao).findOne("admin"); } Mock le DAO Programme le DAO Vérifie l’interaction  A utiliser judicieusement  Lorsque la méthode testée ne renvoie pas de résultat  Pour des problématiques de performance
  • 18.  Partial mocking avec spy  Enregistre les interactions  Simule le comportement de méthodes choisies Boîte à outils : Mockito (3/4) List<String> list = new ArrayList<String>(); List<String> spyList = Mockito.spy(list); spyList.add("one"); assertEquals(1, spyList.size()); Mockito.verify(spyList).add("one"); when(spyList.size()).doReturn(100); assertEquals(100, spyList.size());
  • 19.  ArgumentCaptor Permet de récupérer la valeur des paramètres d’appel d’un mock Boîte à outils : Mockito (4/4) public class Person { private final String name; public Person(String name) { this.name = name; } public String getName() { return name; } public void inviteToParty(Person friend, Party party) { party.addGuest(friend); } } ArgumentCaptor<Person> argument = ArgumentCaptor.forClass(Person.class); Party party = mock(Party.class); Person john = new Person("John"); Person peter = new Person("Peter"); // Peter invites John to the party peter.inviteToParty(john, party); verify(party).addGuest(argument.capture()); // verify John was invited assertEquals("John", argument.getValue().getName());
  • 20.  Tester unitairement des DAO nécessite une base de données embarquée  Les puristes les considèrent comme des tests d’intégration  Spring Test  Facilite le chargement de la configuration Spring liée à l’infrastructure  Prend en charge la création du schéma  Support des transactions  Laisse la base inchangée après l’exécution du test (débrayable)  Gestion manuelle des transactions  Possibilité d’exécuter du code en dehors d’une transaction Tester des DAO (1/3)
  • 21. Tester des DAO (2/3)  Avec DbUnit accounts-dataset.xml <?xml version='1.0' encoding='UTF-8'?> <dataset> <ACCOUNT ID="1" BIC="FR7030002005500000157845Z02" LABEL="Account 1"/> <ACCOUNT ID="2" BIC="FR70300023455000021Z4234Y45" LABEL="Account 2"/> </dataset> public class TestHibernateAccountDao extend AbstractDaoTest<HibernateAccountDao> { @Test public void findAccountByIBan() { DbUnitLoader.loadDataset("accounts-dataset.xml"); String iban = "FR70 3000 2005 5000 0015 7845 Z02"; Account account = dao.findAccountByIBan(iban); assertNotNull(account); assertEquals("Account 1", account.getDescription()); } }
  • 22.  Avec Spring Test Tester des DAO (3/3) Extrait du fichier spring/dao-config.xml <beans profile="test"> <jdbc:embedded-database id="dataSource" type="HSQL"> <jdbc:script location="classpath:create-schema.sql"/> </jdbc:embedded-database> </beans> <beans profile="production"> <jee:jndi-lookup id="dataSource" jndi-name="jdbc/MyDataSource"/> </beans> <bean id="sessionFactory" class="o.s.orm.hibernate4.LocalSessionFactoryBean"> <property name="dataSource" ref=" dataSource " /> … </bean> <bean id="transactionManager" class="o.s.orm.hibernate4.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory" /> </bean> <context:component-scan base-package="com.myapp.dao"/> @ContextConfiguration(locations = {"classpath:spring/dao-config.xml"}) @RunWith(SpringJUnit4ClassRunner.class) @ActiveProfiles("test") public class ClinicDaoTests { @Autowired ClinicDao clinicDao; } @Test @Transactional public void shouldInsertOwner() { Collection<Owner> owners = clinicDao.findOwnerByLastName("Schultz"); int found = owners.size(); Owner owner = new Owner(); owner.setFirstName("Sam"); owner.setLastName("Schultz"); owner.setAddress("4, Evans Street"); owner.setCity("Wollongong"); clinicDao.saveOwner(owner); assertThat(owner.getId().longValue()).isNotEqualTo(0); owners = clinicDao.findOwnerByLastName("Schultz"); assertThat(owners.size()).isEqualTo(found + 1); }
  • 23.  Avec Spring Test @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration({"classpath:spring/mvc-core-config.xml"}) @WebAppConfiguration public class PetControllerTests { @Autowired PetController petController; MockMvc mockMvc; @Before public void setup() { mockMvc = MockMvcBuilders.standaloneSetup(petController) .build(); } @Test public void testProcessUpdateFormSuccess() throws Exception { mockMvc.perform(post("/owners/{ownerId}/pets/{petId}/edit", 1,1) .param("name", "Betty") .param("type", "hamster") .param("birthDate", "2015/02/12") ) .andExpect(status().is3xxRedirection()) .andExpect(view().name("redirect:/owners/{ownerId}")); } } Tester un contrôleur Spring MVC
  • 24. Tester du code legacy  Code faisant appel à un Singleton avec getIntance()  Création d’un setter de visibilité package permettant de passer un mock  Méthode phagocyte  Refactoring en sous méthodes qui seront testées individuellement  Méthode private  Changement de la visibilité en portée package
  • 25. Conclusion  Ecrire des tests, cela s’apprend  Tester unitairement, c’est coder  A chaque couche / techno, sa typologie de test

Notes de l'éditeur

  1. Classement personnel issu d’un certain pragmatisme Test de la configuration Spring s’apparente davantage un test d’intégration mais comme stable et relativement rapide => TU ? Utilisation d’une base de données en mémoire ?
  2. Tests fonctionnels  tests d’acceptance / tests d’IHM HP Fortify permet de tester la vulnérabilité du code à l’égard de failles de sécurité Ces 4 types de tests sont complémentaires. Un TU garantie que le contrat de la classe à tester est respectée mais ne garantie pas que l’application respecte les spécifications fonctionnelles. De part leur nature, les tests d’intégration et les tests fonctionnels sont moins stables : dépendances aux données, à la disponibilité des adhérences, à l’infra …
  3. Objectif premier lorsqu’on ne pratique pas le TDD : trouver des bugs Le TU fait partie du Done en agile Les TU permettent au CP de s’assurer à minima que le développeur à tester son code Se prémunir de régression : penser aux autres développeurs qui maintiendront l’application Un harnais de tests est toujours sécurisant
  4. Documenter le code : Spring REST Docs utilise les tests pour générer la doc de son API web. Contrairement à la JavaDoc, les TU sont toujours à jour. Contribue au design de l’application : un code testable est souvent plus lisible / maintenable (pas de singleton ou de méthodes phagocytes) Design logiciel : couplage faible (IoC), principe de substitution de Liskow (emploi correct de l’héritage), conception orientée service
  5. L’écriture de TU a un coût (souvent estimé à 20% de la charge de dévs) et sa maintenance en a également un, Eviter de tester du code legacy qui ne change jamais
  6. Des TU sans assertions ne servent à rien (mise à part vérifier qu’aucune exception n’est levée) Un TU non exécuté (@Ignore ou EclipseTestXXX) n’est pas maintenu Nom des méthodes de tests : nom à rallonge en camelCase ou avec des _ Messages d’erreur davantage explicite avec AssertJ que JUnit
  7. Qui dit test, dit jeux de données. Code existant difficilement testable ne respectant pas les principes OCP, LSP, KISS, ISP, DIP, SOLID (cf Clean Code)
  8. Exemple inspiré de la documentation Junit. Mais décomposition valable quelque soit la techno. Given When Then fait partie de la méthode agile Behavior-Driven Development http://martinfowler.com/bliki/GivenWhenThen.html Bonne pratique : séparer les blocs given/when/then par une ligne vide
  9. Mock = simulacre Les bouchons ne sont pas des simulacres : http://martinfowler.com/articles/mocksArentStubs.html Les mockes évitent de devoir coder des stubs et les maintenir (par exemple lors d’ajout de méthode dans une interface) Adhérences : DAO, autre service métier … Utilisation des mocks contestées Le NoMock Movement : http://antoniogoncalves.org/2012/11/27/launching-the-nomock-movement/ Mock ou pas Mock ? : https://www.fierdecoder.fr/2015/11/mock-ou-pas-mock/
  10. Autres outils non présentés : AssertJ, assertReflectionEquals pratique dans les cas suivant : Mapping objet / objet Marshalling / Unmarshalling Persistance / rechargement en base
  11. Introduction : Spring encourage le développement en utilisant des POJO. En principe, ceux-ci sont testables sans Spring. Cependant, il peut être tout de même intéressant de s’appuyer sur Spring pour : Profiter de l’injection de dépendance dans les tests, Tester unitairement la configuration Spring Réutiliser la configuration Spring Possibilité de ne pas utiliser le module spring test : création du contexte applicatif dans la méthode setUp Spring 4.2 supporte Junit 4.9 et + Mise en cache : pour tous les tests utilisant le même fichier de configuration Spring Demander à JUnit d’utiliser tel runner passe par l’annotation @RunWith (même principe utilisé par Unitils ou Mockito) Autres annotations : BeforeTransaction, AfterTransaction DirtiesContext : invalide le contexte afin que la prochaine méthode de test reconstruise le context Spring IfProfileValue : exécute un test en fonction de l’évaluation d’une variable système (ex: test spécifique à la plateforme) Autres bouchons : MockJspWriter, MockServletContext, MockPortletSession Spring MVC Test Framework : http://docs.spring.io/spring/docs/current/spring-framework-reference/html/integration-testing.html#spring-mvc-test-framework Les annotations @Sql, @SqlConfig et @SqlGroup ont été introduites dans Spring 4.1
  12. Alternative : DbSetup pour créer des jeux de données en Java
  13. Exemples issus de la documentation officielle : http://mockito.org/
  14. A la place du verify, privilégier plutôt assertEquals(admin, loadedUser); Performance : vérifier le nombre d’appel d’un WS
  15. Plus d’informations : http://www.baeldung.com/mockito-spy
  16. Exemple tiré du blog https://rwehner.wordpress.com/2010/02/23/mockito-some-useful-programming-examples/ D’autres exemples : http://www.programcreek.com/java-api-examples/org.mockito.ArgumentCaptor
  17. Base de données embarques : H2 ou HSQLDB DbUnit (XML) ou DbSetup (Java) Création du schéma : ResourceDatabasePopulator, @Sql, namespace <jdbc:initialize-database/> Gestion manuelle des transactions : TestTransaction Exécution du code en dehors des tx : @BeforeTransaction, @AfterTransaction
  18. Nécessite une surcouche technique. Exemple : Spring Test DbUnit https://github.com/springtestdbunit/spring-test-dbunit Dans cet exemple, la classe abstraite AbstractDaoTest est chargée de : Instancier le DAO à tester Initialiser la base de données embarquée Charger la configuration Hibernate Créer la table ACCOUNT La classe AbstractDaoTest utilise ou non Spring Test La classe DbUnitLoader factorise le code permettant de charger un dataset
  19. Classe de test inspiré de la classe ClinicServiceJpaTests du projet spring-petclinic La méthode assertThat vient de la librairie AssertJ http://joel-costigliola.github.io/assertj/ @Transactional : inutile de nettoyer la base après le test
  20. Extrait de spring-petclinic : https://github.com/spring-projects/spring-petclinic/blob/master/src/test/java/org/springframework/samples/petclinic/web/PetControllerTests.java