Your tests are your first and best line of defense against software defects.
Your tests are more important than linting \& static analysis (which can only find a subclass of errors, not problems with your actual program logic).
Tests are as important as the implementation itself (all that matters is that the code meets the requirement -- how it's implemented doesn't matter at all unless it's implemented poorly).
Design aid: Writing tests first gives you a clearer perspective on the ideal API design.
\item
Feature documentation (for developers): Test descriptions enshrine in code every implemented feature requirement.
\item
Test your developer understanding: Does the developer understand the problem enough to articulate in code all critical component requirements?
\item
Quality Assurance: Manual QA is error prone.
In my experience, it's impossible for a developer to remember all features that need testing after making a change to refactor, add new features, or remove features.
\item
Continuous Delivery Aid: Automated QA affords the opportunity to automatically prevent broken builds from being deployed to production.
La \href{https://fr.wikipedia.org/wiki/Nombre_cyclomatique}{complexité cyclomatique} (ou complexité de McCabe) peut s'apparenter à mesure de difficulté de compréhension du code, en fonction du nombre d'embranchements trouvés dans une même section.
Le code suivant \autoref{cyclomatic-simple-code} a une complexité cyclomatique 1; il s'agit du cas le plus simple que nous pouvons implémenter : l'exécution du code rentre dans la fonction (il y a un seul embranchement), et aucun bloc conditionnel n'est présent sur son chemin.
\caption{Une version ultra-simple de l'affichage du jour de la semaine}
\label{cyclomatic-simple-code}
\end{listing}
Si nous complexifions cette fonction en vérifiant (par exemple) le jour de la semaine, nous aurons notre embranchement initial (l'impression à l'écran de la date du jour), mais également un second embranchement qui vérifiera si cette date correspond à un lundi:
\begin{listing}[!h]
\begin{minted}{Python}
from datetime import date
def print_current_date_if_monday():
if date.today().weekday() == 0:
print("Aujourd'hui, c'est lundi!")
print(date.today())
\end{minted}
\caption{Ajout d'une fonctionnalité essentielle et totalement indispensable}
\textbf{La lisibilité du code}: au plus la complexité cyclomatique sera élevée, au plus le code sera compliqué à comprendre en première instance. Il sera composé de plusieurs conditions, éventuellement imbriquées, il débordera probablement de la hauteur que votre écran sera capable d'afficher
\item
\textbf{Les tests unitaires}: pour nous assurer d'une couverture de code correcte, il sera nécessaire de couvrir tous les embranchements présentés. Connaître la complexité permet de savoir combien de tests devront être écrits pour assurer une couverture complète de tous les cas pouvant se présenter.
Il est important de noter que refactoriser un bloc, par exemple en extrayant une méthode, n'améliore pas la complexité cyclomatique globale de l'application.
Extraire une méthode à partir de ce bloc pourra réduire la complexité de la fonction \mintinline{python}{print_current_date} n'améliorera rien et ne fera que déplacer le problème.
Une solution serait de passer par un dictionnaire, de façon à ramener la complexité à 1:
\begin{listing}[H]
\begin{minted}{python}
from datetime import date
def print_current_date():
DAYS_OF_WEEK = {
0: "Lundi",
1: "Mardi",
2: "Mercredi",
3: "Jeudi",
4: "Vendredi",
5: "Samedi",
6: "Dimanche"
}
print(DAYS_OF_WEEK.get(date.today().weekday()))
print(date.today())
\end{minted}
\caption{La même version, avec une complexité réduite à 1}
De manière générale, si nous nous rendons compte que les tests sont trop compliqués à écrire ou nous coûtent trop de temps, c’est sans doute que l’architecture de la solution n’est pas adaptée et que les composants sont couplés les uns aux autres.
Dans ces cas, il sera nécessaire de refactoriser le code, afin que chaque module puisse être testé
indépendamment des autres. \cite{clean_code}
Le plus important est de toujours corréler les phases de tests indépendantes du reste du travail (de développement, ici), en l’automatisant au plus près de sa source de création:
Martin Fowler observes that, in general, "a ten minute build [and test process] is perfectly within reason...
[We first] do the compilation and run tests that are more localized unit tests with the database completely stubbed out. Such tests can run very fast, keeping within the ten minutes guideline.
However any bugs that involve larger scale intercations, particularly those involving the real database, won’t be found.
The second stage build runs a different suite of tests [acceptance tests] that do hit the real database and involve more end-to-end behavior.
The aim of a unit test is to show that a single part of the application
does what programmer intends it to.
\end{quote}
Les tests unitaires ciblent typiquement une seule fonction, classe ou méthode, de manière isolée, en fournissant au développeur l’assurance que son code réalise ce qu’il en attend.
Pour plusieurs raisons (et notamment en raison de performances), les tests unitaires utilisent souvent des données stubbées - pour éviter d’appeler le "vrai" service
Idéalement, chaque fonction ou méthode doit être testée afin de bien en valider le fonctionnement, indépendamment du reste des composants.
Cela permet d'isoler chaque bloc de manière unitaire, et permet de ne pas rencontrer de régression lors de l'ajout d'une nouvelle fonctionnalité ou de la modification d'une existante.
C'est ici qu'il est utile d'avoir le pourcentage de code couvert par les différents tests, pour savoir ce qui peut être amélioré, le but du jeu consistant simplement à augmenter ou égaler le pourcentage de couverture de code existant avant chaque modification.
Gitlab permet de visualiser cette information de manière très propre, en l'affichant au niveau de chaque proposition d'intégration.
La couverture de code est une analyse qui donne un pourcentage lié à la quantité de code couvert par les tests. Attention qu'il ne s'agit pas de vérifier que le code est \textbf{bien} testé, mais juste de vérifier \textbf{quelle partie} du code est testée.
Le paquet \texttt{coverage} se charge d'évaluer le pourcentage de code couvert par les tests.
Les tests d’acceptance vérifient que l’application fonctionne comme convenu, mais à un plus haut niveau (fonctionnement correct d’une API, validation d’une chaîne d’actions effectuées par un humain, ...).