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’écri...
Sommaire
 Les différents types de tests automatisés
 Objectifs des tests unitaires
 Stratégies de mise en œuvre des tes...
 Tests unitaires
 Teste une méthode d’une classe en isolation du reste de l’application
 Les tests exécutés lors d’un m...
 Tests fonctionnels
 Exécute des scénarios fonctionnels en simulant des interactions utilisateurs
 Outils : Selenium, H...
 Vérifier le comportement d’une fonctionnalité au
regard des exigences
 Tester ses développements
 Fait partie du job d...
 Documenter le code
 Contribuent
 au design logiciel
 à la qualité générale de l’application
Objectifs des tests unita...
 Tester en priorité :
 le code complexe
 les corrections de bugs
 le code sensible (souvent amené à changer)
 Tester ...
 Valider le fonctionnement par des assertions
 Automatiser l’exécution des TU
 Un TU doit être rapide à exécuter
 Essa...
 Jeux de données
 Demande une connaissance fonctionnelle
 Peut-être complexe et fastidieux à initier
 Tester la couche...
 Un test se décompose généralement en 3 étapes
import static org.junit.Assert.assertEquals;
import org.junit.Test;
public...
 Besoin : tester individuellement un service métier,
c’est à dire sans ses adhérences
L’intérêt des mocks
Service
Dao
Ser...
 ReflectionAssert.assertReflectionEquals de unitils-core
Boîte à outils : assertReflectionEquals de Unitils
User user1 = ...
Boîte à outils : Spring Test
 Conteneur léger accessible aux tests unitaires et d’intégration
 Support de JUnit 4 et Tes...
 Permet de charger en base des jeux de données
 A partir de fichier XML
 Favoriser des dataset les plus petits possible...
 Créer un mock
Boîte à outils : Mockito (1/4)
import static org.mockito.Mockito.*;
List mockedList = mock(List.class);
as...
 Vérifier les interactions avec le mock
Boîte à outils : Mockito (2/4)
@Test
public void testAdminAuthentication() {
User...
 Partial mocking avec spy
 Enregistre les interactions
 Simule le comportement de méthodes choisies
Boîte à outils : Mo...
 ArgumentCaptor
Permet de récupérer la valeur des
paramètres d’appel d’un mock
Boîte à outils : Mockito (4/4)
public clas...
 Tester unitairement des DAO nécessite une base de données embarquée
 Les puristes les considèrent comme des tests d’int...
Tester des DAO (2/3)
 Avec DbUnit
accounts-dataset.xml
<?xml version='1.0' encoding='UTF-8'?>
<dataset>
<ACCOUNT ID="1" B...
 Avec Spring Test
Tester des DAO (3/3)
Extrait du fichier spring/dao-config.xml
<beans profile="test">
<jdbc:embedded-dat...
 Avec Spring Test
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"classpath:spring/mvc-core-config.xml"})...
Tester du code legacy
 Code faisant appel à un Singleton avec getIntance()
 Création d’un setter de visibilité package p...
Conclusion
 Ecrire des tests, cela s’apprend
 Tester unitairement, c’est coder
 A chaque couche / techno, sa typologie ...
Prochain SlideShare
Chargement dans…5
×

Tester unitairement une application java

7 028 vues

Publié le

Présente les différents types de tests automatisés, les objectifs des tests unitaires, les stratégies de mise en œuvre, les bonnes pratiques, les difficultés, ce qu'est un mock, différents outils (Unitils, Mockito, DbUnit, Spring Test) et des exemples de tests (DAO et contrôleurs Spring MVC), sans oublier le test de code legacy.

Publié dans : Technologie
  • Soyez le premier à commenter

Tester unitairement une application java

  1. 1. Tester unitairement une application Java 11 février 2016
  2. 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. 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. 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. 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. 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. 7.  Documenter le code  Contribuent  au design logiciel  à la qualité générale de l’application Objectifs des tests unitaires (2/2)
  8. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 25. Conclusion  Ecrire des tests, cela s’apprend  Tester unitairement, c’est coder  A chaque couche / techno, sa typologie de test

×