Blog Arolla

Différents tests pour un·e développeu·r·se

Quand on parle des tests pour les développeurs on pense automatiquement aux tests unitaires. Mais vous savez très bien, comme moi, que ce ne sont pas les seuls tests qu'un·e développeu·r·se peut écrire et réaliser. Mais avant d'énumérer les différents tests disponibles pour un·e développeu·r·se, rappelons les fondamentaux.

A quoi sert un test ? Quand on se réfère au dictionnaire Larousse, un test est un essai d'un produit, d'un appareil pour vérifier son action, son fonctionnement.

Mais nous savons aussi qu'un test vérifie qu'un livrable répond bien au besoin métier. Il instaure donc une confiance au livrable en assurant la non régression et en réduisant les risques. Il permet aussi de vérifier l'intégration des composants ou systèmes. Sans oublier, bien sûr, la capacité d'un test de documenter une fonctionnalité quand il est bien nommé.

Que vous soyez novice pour les tests ou expert, j'imagine que vous avez déjà entendu parler ou assister à des discussions autour des tests unitaires, d'intégration ou les tests système ou de performance. Je vous invite tout au long de cet article à décortiquer ensemble les différents types de tests à disposition d'un·e développeu·r·se à travers une application simple de conversion de montant de devises. L'application et les exemples seront en C#.

Maintenant qu'on sait à quoi sert un test, commençons par énumérer les différents types. Dans les littératures, on tombe souvent sur ce qu'on appelle la pyramide des tests.

Pyramide des tests

Présentation

Comme le décrit Martin Fowler dans son article, elle permet de visualiser les familles de tests. Les niveaux ne sont pas exhaustifs mais ça donne une idée sur les types, le nombre, le coût et le temps d'exécution.

En effet, plus on est près de la base de la pyramide, plus :

  • Le nombre de tests est important
  • Le temps d'exécution est rapide
  • Et le coût est faible

Plus on monte donc et plus le temps d'exécution est long et le coût est important.

Le coût englobe le coût d'écriture du test, le coût d'investigation quand le test échoue et le coût de correction des tests échoués. Et puis, quand le temps d'exécution d'un test est long, le feedback est moins efficace puisqu'on ne pourra pas réagir au plus tôt à toute anomalie.

Vous allez me dire que dans votre équipe ce n'est pas forcément le cas et que vous vivez bien avec. Peut-être mais j'espère que vous allez être convaincus à la fin de cet article de l'inverse 😉

Voyons déjà ce que nous propose cette pyramide :

De la base vers le sommet, on trouve :

  • En premier, les tests unitaires qui représentent généralement les règles de gestion de notre application,
  • Puis les tests d'intégration qui représentent les use-cases de notre application,
  • Ensuite, les tests end to end vont représenter les workflows ou process métier de notre application,
  • Et enfin, les tests utilisateur représentés par l'œil qui sont les tests manuels exploratoires de l'application.

On peut représenter la pyramide des tests sous forme de triangle pour mieux visualiser les axes (coût, temps, nombre).

Malheureusement, sont majoritaires les projets qui ont une forme de pyramide inversée appelée aussi « Cornet de glace ». On y trouve donc les tests unitaires en minorité et les tests exploratoires en grand nombre.

Pour chaque famille, nous verrons ensemble la définition, comment les mettre en place et les outils qui nous aideront à les automatiser potentiellement.

Tests unitaires (Unit test, UT)

Définition

C'est un test qui permet de valider le comportement d'une unité de code. La définition d'une unité de code est propre à chacun. Il y a une école qui considère l'unité comme une classe et une autre qui considère que l'unité est une fonctionnalité et donc peut correspondre à plusieurs classes.

Quoi qu'il en soit un test unitaire doit respecter les critères FIRST :

  • Fast : il doit être rapide, on peut le lancer à tout moment sans que ça prenne du temps. Ça doit être dans l'ordre des secondes et non pas des minutes.
  • Isolated / Independent : il ne faut pas qu'il y ait un ordre d'exécution des tests. Un test doit être indépendant de tous les tests qui l'entourent. Et une seule cause doit être à l'origine de son échec et non plusieurs.
  • Repeatable : un test doit être déterministe. Si je le lance plusieurs fois avec une même donnée en entrée le résultat ne doit jamais changer.
  • Self validating : le test doit faire la validation lui-même. Pas besoin donc d'intervention manuelle pour savoir s'il est passé ou a échoué.
  • Timely / thorough : le test doit être minutieux car il doit être précis par rapport à ce qu'il teste. Le test doit être écrit au moment opportun. En appliquant la méthode TDD (Test Driven Development), on va écrire le test avant le code de production et donc l'écriture sera au moment où on en a besoin. Le test peut être écrit après l'écriture du code dans le cadre d’un code legacy (code sans test) par exemple.

Un test doit respecter la règle des 3A (Arrange/Act/Assert) :

  • Arrange : correspond à l'initialisation du contexte du test
  • Act : correspond à l'appel à la fonctionnalité à tester
  • Assert : correspond à la vérification du résultat

Comment mettre en place

Les frameworks de tests unitaires sont multiples sur le marché, pour tous les goûts et pour presque tous les langages aujourd'hui. Du Nunit pour C# au Junit pour Java jusqu'au PHPunit pour PHP ou même zunit pour COBOL. Donc dites-vous que vous n'avez plus d'excuses pour ne pas en créer 😉

La liste est longue et n'est pas exhaustive car même pour un seul langage on peut avoir plusieurs frameworks. Prenons le cas de C#, nous trouverons Nunit, xUnit, MSTest et d'autres encore.

Via l'IDE (Integrated Development Environment), par simple commande on peut lancer un ou plusieurs tests à la fois.

Voici donc un premier test unitaire avec MSTest :

[TestMethod]
public void Should_convert_the_amount_when_given_currency_and_rate()
{
// Arrange
var eur = new Currency("EUR");
var euroAmount = new Amount(10, eur);
var usd = new Currency("USD");
var eurUsdRate = new Rate(1.14);
// Act
var usdAmount = euroAmount.Convert(usd, eurUsdRate);
// Assert
var expectedAmount = new Amount(11.4, usd);
Check.That(usdAmount).IsEqualTo(expectedAmount);
}

Tests d’intégration (Integration test, IT)

Définition

D'après Martin Fowler, un test d'intégration détermine si les composants d'une application continuent à fonctionner correctement quand on les connecte ensemble. Il distingue deux types de tests d'intégration :

  • Narrow Integration tests (étroite couverture d'unités) : leur scope s'arrête à quelques composants tout en substituant d'autres dépendances. Si la notion de substitution ou mock vous est étrangère, je vous invite à me suivre car je vais écrire un autre article là-dessus. Ces tests ressemblent largement à des tests unitaires au niveau de l'utilisation des frameworks et la façon de les écrire. Dans l'exemple suivant, le convertisseur appelle l'implémentation réelle d'Amount par contre on crée un substitut au fournisseur des taux (rates) :
[TestMethod]
public void Should_convert_the_amount_when_target_currency_is_different()
{
var usdCurrency = new Currency("USD");
var eurCurrency = new Currency("EUR");
var eurAmount = new Amount(10, eurCurrency);
IRates rates = Substitute.For<IRates>();
var eurUsdRate = new Rate(1.14);
rates.GetRateOf(usdCurrency).Returns(eurUsdRate);
var converter = new Converter(rates);
var convertedAmount = converter.Convert(eurAmount, usdCurrency);
var expectedAmount = new Amount(11.4, usdCurrency);
Check.That(convertedAmount).IsEqualTo(expectedAmount);
}
  • Broad Integration tests (couverture étendue d'unités) : les tests qui vont avoir besoin d'instances réelles de services, potentiellement d'une base de données ... l'idée est d'exécuter un scénario d'un plus large scope que celui utilisant les substituts. Ainsi on s’assure que l'intégration des composants se prépare au mieux pour éviter les surprises aux tests utilisateur. En voici un exemple :
[TestMethod]
public void Should_convert_the_amount_when_target_currency_is_different ()
{
Currency usdCurrency = new Currency("USD");
Currency eurCurrency = new Currency("EUR");
Amount eurAmount = new Amount(10, eurCurrency);
IRates rates = new Rates();
Converter converter = new Converter(rates);
Amount convertedAmount = converter.Convert(eurAmount, usdCurrency);
Amount expectedAmount = new Amount(11.4, usdCurrency);
Check.That(convertedAmount).IsEqualTo(expectedAmount);
}

Vous remarquerez que la différence réside dans l'utilisation d'un substitut pour le narrow test et l'utilisation de l'implémentation réelle dans le broad test ce qui fait que le deuxième test est plus profond que le premier.

Un test d'intégration peut très bien être aussi sous format Gherkin. Il s'agit d'un formalisme "given/when/then" qui structure et rapproche le test au langage humain. Ce formalisme facilite l'écriture des tests avec le PO (Product Owner) ou les utilisateurs puisqu'il se rapproche d'une spécification ou une documentation. On l’utilise souvent dans le cadre d’une méthodologie BDD (Behavior Driven Development).

Ci-dessous vous trouverez un exemple de scénario. Chaque ligne correspond à une "step definition" qui sera associée à une méthode (et donc une implémentation). Cette dernière sera exécutée quand l'étape (appelée aussi step) sera atteinte et ainsi elle détermine si le test a réussi ou a échoué.

Scenario: Convert amount if the rate is found

Given I have 100 EUR

And I have entered USD as target currency

And the rate is 11.4

When I convert amount

Then the converted amount should be 114 USD

 

Scenario: Convert amount if the rate is not found

Given I have 100 EUR

And I have entered TOG as unfound target currency

When I convert amount

Then I should get an error

Comment mettre en place

Les tests d'intégration s'écrivent avec les mêmes outils que ceux des tests unitaires. On peut donc utiliser les substituts pour un ou plusieurs composants et utiliser les implémentations de production pour d'autres.

Si on ne veut pas utiliser les substituts alors que notre code doit accéder à une dépendance externe, on a plusieurs pistes.

  • Cas où on doit appeler une API REST par exemple. Dans ce cas il nous faut un environnement prêt ("un environnement up" comme on dit dans le milieu) avec la version à tester du service. Bien sûr ce n'est pas le service de la production, mais ça peut être la même version.
  • Cas où on a besoin d'accéder à une base de données. Dans ce cas il nous faut une base de données pour les tests. Cette base va être mise à jour potentiellement tous les soirs ou périodiquement. Il faudra définir la stratégie au niveau de l'équipe et l'importance d'un tel investissement, car il ne faut pas oublier que le schéma de la base évolue sans cesse et les données aussi. Comme le test doit être dépendant d'eux, il faudra penser à insérer les données du test, le faire tourner et puis nettoyer les données afin qu’au lancement suivant le même test ne soit pas altéré.

Ne vous inquiétez pas, il y a une autre solution pour lancer le test sans devoir déployer une base. On peut utiliser dans ce cas, ce qu'on appelle les bases in-memory. Il s'agit d'une base en mémoire qui sera supprimée quand tous les tests auront fini leur exécution. On ne règle pas le problème des schémas de la base qui évoluent, mais on aura un problème en moins.

Pour le format Gherkin, on peut très bien utiliser SpecFlow (https://specflow.org/) pour .Net ou Cucumber (https://cucumber.io/) pour java.

Tests de bout en bout (End To End, E2E)

Définition

Ces tests permettent de vérifier que l'ensemble des composants d'une fonctionnalité fonctionnent de bout en bout, de la partie IHM (Interface Homme Machine) aux dépendances externes (base de données ou service externe comme une API REST). On utilise généralement ces tests pour les scénarios critiques d'une application. Leur coût est important comme montré dans la pyramide des tests d'où le fait que leur nombre est inférieur à celui des tests d'intégration et unitaires. Il vaut mieux donc bien les cibler avec le PO ou les utilisateurs pour mieux les identifier et ne pas perdre du temps à créer des tests pour des scénarios non critiques. Gardez en tête que si les choses sont bien faites alors les scénarios non critiques seront couverts par les tests d'intégration et unitaires.

Comment mettre en place

Il y a plusieurs outils permettant d'écrire des tests E2E. Selenium (https://www.seleniumhq.org) fait partie des plus populaires sur le marché.

Lors de l'écriture de cet article, j'ai développé une application exemple en dotnet core. Et l'utilisation de Selenium est assez simple. Il suffit de référencer ces packages nugget :

<PackageReference Include="Selenium.Support" Version="3.141.0" />

<PackageReference Include="Selenium.WebDriver" Version="3.141.0" />

<PackageReference Include="Selenium.WebDriver.MicrosoftDriver" Version="17.17134.0" />

Suivant le navigateur à utiliser pour les tests, on peut rajouter les packages correspondants.

Dans mon cas, j'ai utilisé le EdgeDriver.

using (var driver = new EdgeDriver(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)))

Ensuite, il faut naviguer vers la page qu'on veut tester avec la méthode :

driver.Navigate().GoToUrl(@"https://mysite/Home");

Pour setter la valeur des champs, il suffit de récupérer l'élément via son identifiant et insérer la valeur :

var element = driver.FindElement(By.Id(elementId));

element.SendKeys(elementValue);

Pour récupérer la valeur d’un champ par exemple, on appelle tout simplement la méthode GetAttribute("value") de l'élément :

var convertedAmount = driver.FindElement(By.Id("ConvertedAmount"));

var convertedValue = convertedAmount.GetAttribute("value");

Il n'y a plus qu'à vérifier la valeur via une assertion.

Tests exploratoires

Définition

Les tests exploratoires sont des tests exécutés manuellement. Leur rôle principal est de trouver les bugs qui ne sont pas détectés par les autres familles de test afin d’améliorer la stabilité et l’utilisabilité de l’application. Mais au-delà de la validation, ces tests permettent aussi l’apprentissage et la compréhension du produit. Ça permet ainsi d’avoir une idée du produit et de trouver de nouvelles fonctionnalités. Généralement, ils ne sont pas basés sur des documents et donc non dirigés et leur temps d’exécution peut durer des jours et des jours.

Pour encore plus de qualité

Quand on veut viser plus de qualité pour notre application, on peut réaliser d'autres types de tests qui pourront se greffer à tous les niveaux de la pyramide. Il s'agit plus d'une caractéristique à mon sens. On trouve donc :

  • L'automatisation des tests
  • Les tests de performance ou de charge
  • Les tests de vérification en service régulier

Automatisation des tests

Une fois les tests créés, on peut les laisser dans le projet et les lancer manuellement à chaque modification du code, comme on peut automatiser leur exécution en local en utilisant le live-testing de votre IDE (si l'option existe) ou dans un terminal à côté. Il ne faut pas oublier non plus la CI (Continuous integration comme Jenkins ou TeamCity), il suffit de configurer une step de tests après la compilation. Ça permettra donc à chaque push de lancer tous les tests et voir ainsi l'état de l'usine au fur et à mesure du développement.

Le live-testing est très utile lors du développement puisqu'à chaque enregistrement ou compilation tous les tests présents et concernés par la modification vont être lancés. On voit ainsi si une régression est apportée à condition qu'on ait, bien sûr, une bonne couverture de tests. La photo ci-dessous (live-testing de Visual Studio) présente un code couvert par sept tests et que tous les tests passent avec succès.

Si on décide de mettre en commentaire une ligne, comme le montre la photo ci-dessous, à l'enregistrement ou à la compilation, tout de suite le statut change en indiquant que certains tests échouent à cause de la dernière modification :

Tests de performance

Ils sont aussi appelés tests de charge ou "stress tests". Ils permettent de s'assurer que l'application supporte bien une certaine charge (de données, d'appels …) en restant réactive et stable. Avant de commencer à mettre en place ce type de test, il faut commencer par définir les objectifs de ces tests et pourquoi on a besoin de mettre en place des tests de performance.

  • Quel est le temps de réponse maximal attendu en secondes ou millisecondes (temps de réponse serveur et temps d'affichage) ? Le temps de réponse exigé d'une page web ne sera pas forcément le même que celui d'un écran d'un client lourd.
  • Quelles sont les modules ou composants qui rentrent dans le scope des tests de performance ?
  • Quel est le nombre d'utilisateurs sur tel ou tel module ou composant ? Deux informations sont intéressantes dans ce cas : le cas nominal (activité classique) et le cas critique (pic d'activités).

Les tests de performance peuvent, bien sûr, être automatisés et lancés dans l'usine d'intégration continue afin de vérifier la non régression des performances de l'application.

On peut commencer par utiliser l’outil DevTools du navigateur s’il s’agit d’un service web pour calculer le temps de réponse par exemple. La photo ci-dessous montre bien que l'appel l'API Convert a mis 180,51 ms. Ça donne ainsi une idée, par rapport aux objectifs déterminés en amont, s’il faut mener ou non une action d'optimisation du code en question.

Votre IDE peut aussi vous aider à investiguer, la photo ci-dessous montre l'outil de diagnostic de Visual Studio lors du debug de l'application. Ça affiche les évènements lancés, l'état d'utilisation de la mémoire et du CPU. Ainsi on peut prendre des actions si nécessaire pour agir au mieux pour l'optimisation de notre application.

Pour des tests plus possés on peut aller au-delà en utilisant des outils plus poussés tels qu’Apache Bench (https://httpd.apache.org/docs/2.4/programs/ab.html) ou Siege (https://www.joedog.org/siege-home/) pour la partie serveur.

Côté client web on trouve, par exemple, SiteSpeed (https://www.sitespeed.io/) qui analyse votre site par rapport aux bonnes pratiques en matière de métriques de performance ou WebPageTest (https://www.webpagetest.org/) qui est gratuit et qui teste une page sur le navigateur choisi et remonte des informations précises sur les performances de la page (comme le montre l'image ci-dessous).

Tests de vérification en service régulier

Une fois que l'application est livrée en production, il est important de vérifier qu'elle est "up" et fonctionnelle. La vérification peut se faire manuellement en lançant, par exemple, tous les matins ou x temps l'application, mais elle peut très bien être automatisée. On peut bien sûr utiliser des tests End To End avec Selenium, par exemple, pour voir si la page se charge bien. En plus, on peut aussi faire des "health checks" de nos APIs. L'idée en tout cas est de vérifier que le service répond bien avec un statut 200.

Dans l'application exemple créée pour cet article, j'ai juste créé un test qui fait appel à notre page d'accueil et vérifie qu'elle répond bien HttpStatusCode.OK qui le code http 200.

[TestMethod]
[TestCategory("Health check test")]
public void Check_that_web_site_is_up()
{
const string url = @"https://mysite/Home";
bool isServiceUp;
try
{
var myRequest = (HttpWebRequest)WebRequest.Create(url);
var response = (HttpWebResponse)myRequest.GetResponse();
isServiceUp = response.StatusCode == HttpStatusCode.OK;
}
catch (Exception)
{
isServiceUp = false;
}
Check.That(isServiceUp).IsTrue();
}

Et plus encore de qualité

Quadrant test agile

Initié par Brian Marick et repris par Lisa Crispin et Jane Gregory dans "Agile Testing", ce quadrant présente les différents types de test dont a besoin un projet agile.

Ce quadrant peut être visualisé de deux façons différentes mais complémentaires. De part et d'autre de la ligne horizontale on trouve une vision métier (Business facing Q2 et Q3) dont les tests sont généralement conçus par les testeurs et une vision technique (Technology facing Q1 et Q4) dont les tests sont implémentés par les développeurs.

Et par rapport à la ligne verticale, à gauche (Q1 et Q2) on trouve les tests pour guider l'équipe et prévenir les défauts en vérifiant l'attendu (Check for the expected) et les bugs et à droite (Q3 et Q4) les tests pour critiquer le produit et détecter les éventuels défauts en cherchant l'inattendu (Looking for the unexpected).

Property based testing

Quand on crée des tests, généralement on fige les données d'entrée (inputs) pour vérifier que le résultat est bien calculé. Avec le property based testing, on va vérifier les propriétés du code avec des valeurs multiples et aléatoires en entrée.

Prenons le cas où on a 10€ qu'on veut convertir en dollars avec un taux de 1.14. On doit avoir en résultat 11.4$. On parle donc d'input et d'output. Ayant ces données d'entrée (10€ et 1.14), on doit avoir le résultat 11.4$.

En property based testing, on parle de données d'entrée (multiples et aléatoires) et de comportement (behavior) attendu. L'idée est donc de spécifier le comportement de la règle de gestion au sein du test.

Classiquement notre test ressemble à ceci :

[TestMethod]
[TestCategory("Unit test")]
public void Should_convert_the_amount_when_having_currency_and_rate()
{
Currency eur = new Currency("EUR");
Amount euroAmount = new Amount(10, eur);
Currency usd = new Currency("USD");
Rate eurUsdRate = new Rate(1.14);
Amount usdAmount = euroAmount.Convert(usd, eurUsdRate);
Amount expectedAmount = new Amount(11.4, usd);
Check.That(usdAmount).IsEqualTo(expectedAmount);
}

Avec le property based testing notre test ressemblera plus à ça :

[Property]
public Property Should_convert_the_amount_using_rate(decimal amountValue, decimal rate)
{
Func<bool> codeToCheck = () =>
{
Currency eur = new Currency("EUR");
Amount euroAmount = new Amount(amountValue, eur);
Currency usd = new Currency("USD");
Rate eurUsdRate = new Rate(rate);
Amount usdAmount = euroAmount.Convert(usd, eurUsdRate);
return usdAmount.HasCurrency(usd) && usdAmount.HasValue(amountValue * rate);
};

return codeToCheck.When(rate != 0);
}

Ce test sera donc lancé plusieurs dizaines voire centaines de fois en ayant à chaque fois des valeurs différentes pour le montant et le taux. L'image suivante montre que le test est lancé 100 fois avec des valeurs différentes et tous ont réussi.

En cas d'erreur, un message est affiché pour annoncer le nombre de tests passés avant d'atteindre l'échec.

Sur le marché et suivant le langage, on trouve beaucoup de librairies pour le property based testing. Pour cet exemple, j'ai choisi FsCheck qui fonctionne aussi bien avec C#, F# ou VB.net, mais pour java on trouve JUnitQuickcheck

Mutation testing

La technique de « mutation testing » permet de vérifier si nos tests sont de bonne qualité.

Certains sont convaincus qu'une bonne couverture de code (vers les 80%) suffit comme indicateur de la bonne qualité du code. Sauf qu'on oublie que c'est une métrique et on peut très bien jouer avec pour avoir le résultat qui nous arrange. Prenons un exemple :

[TestMethod]
public void Should_currency_be_equal_when_having_same_name()
{
Currency eur = new Currency("EUR");
Currency eurCurrency = new Currency("EUR");
Check.That(eur).IsEqualTo(eurCurrency);
}

Quand on lance le calcul de la couverture de code pour ce test, on a un résultat à 100 % de la classe.

Si je remplace le code de mon test par ce qui suit, la couverture de code reste toujours à 100% et pourtant je n'ai pas d'assertion.

[TestMethod]
public void Should_currency_be_equal_when_having_same_name()
{
Currency eur = new Currency("EUR");
Currency eurCurrency = new Currency("EUR");
var areEqual = eur.Equals(eurCurrency);
}

Pour éviter de tomber dans ce genre de piège, la mutation testing vient pour créer des mutants de notre code. Elle va donc lancer le test avec plusieurs versions (mutants) du même code. Chaque mutant correspond à une modification simple comme changer une addition par une soustraction ou une égalité par une différence ou modifier les valeurs de constantes …

Les outils et librairies de mutation testing sont nombreux sur le marché. On peut donc utiliser Stryker ou NinjaTurtles pour .Net ou PI) pour Java et tant d'autres …

On peut le faire aussi manuellement. Rappelons-nous, un mutant c'est un code qui contient une petite modification. Donc on peut juste modifier le code et relancer les tests tout simplement. Ainsi on transforme le code en mutant manuellement. Une fois le code modifié et les tests lancés, s’il n'y a pas de test rouge, cela signifie deux choses :

  • Soit le code modifié n'est pas couvert par des tests et donc c'est l'occasion d'en rajouter.
  • Soit les tests ne sont pas pertinents et donc il vaut mieux les corriger.

Attention, il ne faut pas oublier de remettre le code de production comme il était bien sûr 😉

Avant de finir

Vous pouvez trouver la solution avec tous les tests sur mon github https://github.com/iAmDorra/CurrencyConverter. Pour faciliter le parcours et l'identification des tests, je les ai catégorisés. Vous trouverez donc les unit tests, narrow integration tests, broad integration tests, end to end tests, les health check tests et les property-based tests.

Conclusion

J'espère que vous avez une meilleure connaissance des différents tests qu'un développeur peut implémenter. A vous de jouer avec maintenant et n'oubliez pas, créez à minima autant de tests unitaires que de règles de gestion dans votre application (n’oubliez pas les cas limites), créez des tests d'intégration pour éviter au plus tôt les problèmes d'incompatibilité de composants et surtout choisissez toujours en équipe les tests E2E à écrire car ils sont plus coûteux à créer et maintenir.

Mais avant de vouloir appliquer tous les tests sur votre projet, comme l'a bien présenté Nicolas Fédou lors de sa conférence à Flowcon'18, commencez par réfléchir à la stratégie de tests pour en faire moins mais mieux. C'est la meilleure façon de montrer qu'une application est de qualité suffisante pour aller en production. Il s'agit donc d'un raisonnement avec des choix et des renoncements pour développer un meilleur produit agile.

See you 😉

Plus de publications

6 comments for “Différents tests pour un·e développeu·r·se

  1. Laurent
    30 janvier 2020 at 22 h 28 min

    Merci pour l’article.
    Pour ajouter une couche de tests en haut de la pyramide, on pourrait aussi évoquer les “consumer driven contract tests”.

    https://martinfowler.com/articles/consumerDrivenContracts.html

  2. Dorra
    20 février 2020 at 19 h 36 min

    Merci Laurent pour le partage

  3. 5 octobre 2020 at 13 h 39 min

    Bonjour et merci pour cet article.
    De ma compréhension actuelle du sujet, j’ai l’impression que les exemples des TI ressemblent plus à ce que moi je mets derrière la notion de TU, le narrow étant pour moi presque un TU de Mockist et le broad apparaissant plus comme un test de Classicist. J’ai l’impression que M. Fowler met derrière la notion de TI plutôt ce qu’il appelle un “Component Test”.
    Pour préciser, en archi hexagonale il me semble qu’un classicist testerait avec les TUs tout le code métier en utilisant des “doubles” pour les adapters, et les TIs seraient des tests au niveau adapter, avec par exemple des wiremocks pour les partenaires extérieurs et une base de donnée embarquée.
    Je me trompe ?

  4. Dorra BARTAGUIZ
    14 octobre 2020 at 12 h 14 min

    Bonjour David, merci pour ton commentaire. Tu mets, en effet, le doigt sur un vieux débat infini. Quelle est la définition d’une unité 🙂
    J’ai vu deux écoles. Celle qui considère que l’unité est une classe et donc va substituer toutes les dépendances de la classe dans les tests.
    La deuxième école considère l’unité comme un ensemble de classes qui composent la fonctionnalité à tester.

  5. Cédric Teyton
    17 mars 2021 at 13 h 08 min

    Merci pour l’article !
    Pour aller plus loin sur les tests exploratoires, je peux vous inviter à jeter un oeil sur ce projet 😉 https://github.com/labri-progress/AIFEX

  6. Dorra BARTAGUIZ
    3 novembre 2021 at 16 h 12 min

    Merci Cédric pour le lien du projet 🙂
    C’est intéressent ! Je vais l’expérimenter.