# Qualification et intégration continue
William Petit - S.C.O.P. Cadoles
---
## Qu'est ce que la qualification ?
---
## Qu'est ce que l'intégration continue ?
---
## Les tests applicatifs
Les tests applicatifs ont pour objectif de valider le bon fonctionnement de l'application d'un point de vue métier, c'est à dire de vérifier que celle ci répond aux attentes et aux contraintes du client d'un point de vue fonctionnel.
Ils permettent également et surtout aux développeurs de **détecter les régressions** dans le cycle de développement.
---
## La pyramide des tests applicatifs
![center 70%](./img/pyramidetestsapplicatifs.png)
---
## Tests unitaires
---
## Objectifs
Tester le bon fonctionnement des plus petites unités logiques/fonctionnelles de l'application: les fonctions/méthodes d'objets.
**Exemple** Vérifier qu'une méthode de calcul du prix taxé d'un produit retourne la valeur attendue en fonction des paramètres passés.
---
## Cycle de vie
1. Définir le comportement attendu de la classe/fonction à implémenter.
2. Créer le cas de test validant ce comportement.
3. Exécuter le test. Il ne devrait pas être passant, l'implémentation étant incomplète à ce stade.
4. Implémenter la fonction/classe pour que le test soit passant.
---
## Caractéristiques clés
- Ils sont nombreux.
- Ils sont mis en place dès le début de l'implémentation, voir avant (voir "Test Driven Development").
- Ils sont rapides à exécuter.
- Ils permettent de détecter rapidement les régressions.
- Ils permettent de rassurer le développeur qui doit apporter des évolutions à une base de code existante.
- Ils s'exécutent en continu sur le poste du développeur et à chaque changement sur le serveur de gestion des sources.
- Ils ne devraient pas nécessiter de dépendances externes.
---
## Exemple: Tests unitaires en Javascript (Mocha)
```javascript
// Fichier test/my-test-suite.js
const assert = require('assert');
// Déclaration de la suite de tests
describe('My suite', function() {
// Test synchrone
it('should not fail', function() {
assert.equal(true, 1); // true == 1 en javascript
});
// Test asynchrone
it('should do something async', function() {
return doSomethingAsync()
.then(result => {
assert.ok(result); // On vérifie que le résultat n'est pas null
})
.catch(err => {
assert.ifError(err); // On vérifie qu'aucune erreur n'est remontée
})
;
});
});
```
---
## Et le style dans tout ça ?
- Javascript: https://eslint.org/
- PHP: https://github.com/phpro/grumphp
---
## Exercice: Tests unitaires d'une fonction de validation d'un numéro de carte bancaire en Javascript
Soit un numéro de carte bancaire donné, implémenter un test pour la fonction validant le code de celle ci PUIS implémenter la fonction qui permet de valider ce code.
**Ressources**
- [Mocha - Librairie de test](https://mochajs.org/)
- [Module `assert`](https://nodejs.org/api/assert.html)
- [La formule de Luhn](https://fr.wikipedia.org/wiki/Formule_de_Luhn)
---
## Tests d'intégration
---
## Objectifs
Tester la bonne communication et interaction des différents "modules" composant une application.
**Exemple** Vérifier que qu'une transaction est correctement enregistrée dans la base de données lorsque le module de paiement est utilisé.
---
## Caractéristiques clés
- Ils sont moins nombreux que les tests unitaires.
- Ils peuvent prendre un peu de temps à s'exécuter.
- Ils sont mis en place au cours de l'implémentation initiale et maintenus au fur et à mesure de l'évolution de l'application.
- Ils peuvent nécessiter des dépendances externes, mais généralement pas l'ensemble de l'infrastructure.
- Ils peuvent nécessiter des données d'amorçage.
- Ils impliquent souvent la mise en place de composants "factices" pour simuler une partie de l'application.
- Ils s'exécutent à la demande sur le poste du développeur et sur le serveur d'intégration continue à intervalles réguliers.
---
## Cycle de vie
Les tests d'intégration devraient être abordés (en général) en seconde moitiée d'itération, au moment de la finalisation des modules applicatifs prêts à être livrés.
Ils devraient cibler en premier les modules critiques de l'application, notamment ceux ayant pour rôle de contrôler/valider le cycle de vie des données métiers.
Les tests d'intégration peuvent être un bon témoin/validateur de l'état de réussite d'une itération.
---
## Exemple: Test d'une API REST avec Symfony3
1. Implémenter une API JSON/REST basique avec Symfony3 permettant d'enregistrer/lister dans une base de données (sqlite3) une "annonce" avec les propriétés suivantes:
- Titre
- Description
- Type
- Prix
2. Implémenter avec la librairie standard de Symfony3 les tests suivants:
- Ajout d'une nouvelle annonce et vérification du bon enregistrement de la nouvelle annonce via un appel sur l'URL d'affichage des annonces
**Ressources**
- [Symfony3 - Working with the Test client](http://symfony.com/doc/current/testing.html#working-with-the-test-client)
---
## Tests fonctionnels
---
## Objectif
Tester le bon fonctionnement de l'application d'un point de vue utilisateur.
---
## Caractéristiques clés
- Ils devraient être les moins nombreux de l'ensemble des tests applicatifs.
- Ils devraient être représentatifs des cinématiques d'action des utilisateurs.
- Ils devraient s'exécuter sur un environnement identique à la production.
- Ils sont souvent complexes à mettre en place/maintenir.
- Ils ont une couverture fonctionnelle très large mais ne permettent pas d'identifier directement les sources de dysfonctionnement.
- Ils devraient couvrir les procédures faisant intervenir les dépendances externes de l'application (serveur de courriel, API externes, base de données...).
---
## Cycle de vie
Les tests fonctionnels devraient être implémentés une fois que l'itération a atteint une certaine stabilité en terme d'interface utilisateur.
Ils ne devraient être modifiés que lorsque le client demande une évolution de l'interface utilisateur et/ou des cinématiques d'action.
---
## Exemple: Tests fonctionnels Web avec NightmareJS
Voir l'exemple `tests-fonctionnels`
---
## Exercice: Simuler un utilisateur avec le client web KiwiIRC
Via NightmareJS et avec le client [KiwiIRC](https://kiwiirc.com/nextclient/), se connecter au serveur `irc.freenode.net`, rejoindre le canal `#cadoles` en tapant la commande `/join
gitlab-runner exec docker [job]
> La fonctionnalité est dépréciée depuis la version 10.0. Voir la discussion https://gitlab.com/gitlab-org/gitlab-runner/issues/2797 pour le futur remplacement.
---
## Exercice: Mise en application générale
Sélectionner une application existante (ou laissez le formateur vous proposer un sujet) et:
1. Mettre en place l'exécution des tests unitaires sur le projet et écrire une première suite de tests.
2. Mettre en place les tests d'intégration sur le projet et écrire une première suite de tests.
3. Mettre en place les tests fonctionnels et écrire une première suite de tests.
4. Concevoir un "pipeline" d'intégration continue avec Gitlab et Gitlab Runner et tester une activation complète du pipeline.
---
# Licence
## CC BY-NC-SA 3.0 FR
[Creative Commons - Attribution - Pas d’Utilisation Commerciale - Partage dans les Mêmes Conditions 3.0 France](https://creativecommons.org/licenses/by-nc-sa/3.0/fr/)