Blog ENI : Toute la veille numérique !
🐠 -25€ dès 75€ 
+ 7 jours d'accès à la Bibliothèque Numérique ENI. Cliquez ici
Accès illimité 24h/24 à tous nos livres & vidéos ! 
Découvrez la Bibliothèque Numérique ENI. Cliquez ici
  1. Livres et vidéos
  2. ASP.NET Core MVC
  3. Les tests
Extrait - ASP.NET Core MVC Maîtrisez ce framework web puissant, ouvert et multiplateforme (3e édition)
Extraits du livre
ASP.NET Core MVC Maîtrisez ce framework web puissant, ouvert et multiplateforme (3e édition) Revenir à la page d'achat du livre

Les tests

Introduction

Les tests sont un moyen incontournable pour un projet de vérifier que le code est toujours fonctionnel et qu’il ne comporte pas de bug lors du développement du site. Dans un esprit d’intégration continue et de build automatique de la solution, les tests sont là pour valider que chaque nouvelle partie de code n’a pas intégré de nouveaux bugs dans le système.

ASP.NET Core a été conçu dans le but d’améliorer la testabilité du code de l’utilisateur et ainsi augmenter la maintenabilité de l’application. Les tests automatisés sont ainsi un bon moyen pour le développeur de vérifier que le code qu’il a écrit fait bien ce qu’il doit faire. Il existe bon nombre de types de tests, comme les tests d’intégration, les tests de montée en charge, les tests fonctionnels et bien d’autres encore. Les tests qui nous intéressent dans cette partie concernent les tests unitaires, c’est-à-dire des tests courts et simples permettant de valider des petits morceaux de code.

Les tests unitaires peuvent suivre une bonne pratique intitulée Test Driven Development consistant à écrire d’abord les tests puis à développer les fonctionnalités qui valident les tests, mais nous n’allons pas nous attarder sur ce sujet. Il faut garder...

Les tests serveur avec xUnit

Écrire des tests unitaires n’est pas si simple, et le développeur doit bien concevoir ses classes, ses méthodes et découpler le plus possible son architecture afin de rendre ses tests les plus simples possible. Les dépendances d’une classe peuvent être occultées en suivant le principe Explicit Dependencies Principle et en utilisant ainsi l’injection de dépendances : le découplage est alors assuré.

Une bonne pratique est de séparer les tests dans un projet à part du projet contenant les classes. Un projet de test est simplement une librairie de classes référençant un lanceur de tests (un test runner) et le projet à tester (intitulé le System Under Test, ou SUT). La convention d’ASP.NET va même plus loin en séparant les projets de tests et les projets SUT dans des dossiers à part :

global.json 
MonProjet.sln 
src/ 
  MonProjet/ 
    Startup.cs 
    Services/ 
      MonService.cs 
test/ 
  MonProjet.UnitTests/ 
    Services/ 
      MonService_Test.cs 

Un projet de test doit d’abord être configuré afin d’indiquer quel framework de test il doit utiliser. Le framework conseillé par l’équipe d’ASP.NET Core est xUnit. Il suffit alors de le rajouter dans le projet.json du projet de test.

"dependencies": { 
  "MonProjet": "1.0.0", 
  "xunit": "2.1.0", 
  "dotnet-test-xunit": "1.0.0-rc2-build10025" 
} 

La première dépendance permet évidemment de tester les classes dans notre projet SUT. La deuxième dépendance permet d’ajouter le test runner au projet de test. Enfin, la dernière dépendance permet de lancer les tests avec la ligne de commande dotnet test.

Il existe plusieurs types de tests avec xUnit :

  • Facts : tests qui sont toujours vrais et qui testent des conditions invariantes.

  • Theroy : tests qui ne sont vrais que pour un jeu de données bien particulier....

L’art du Mock

Lors de l’écriture d’un test unitaire, le développeur fait souvent face à diverses dépendances qu’il doit résoudre afin de tester son petit morceau de code.

Rappelons que les tests unitaires ne doivent tester que de petites parties de code, sans se soucier de l’infrastructure en place.

Il n’est donc pas évident de tester une classe qui possède un nombre conséquent de dépendances, surtout qu’avec l’injection de dépendances fournie par ASP.NET Core, l’arbre de dépendances peut vite prendre des proportions bien trop importantes. De plus, lors de l’écriture d’un test, le développeur ne veut pas forcément tester en profondeur des fonctionnalités de log ou de connexion à la base de données : le but est de mettre à disposition des données pour le test, sans forcément se soucier d’où elles viennent tant qu’elles satisfassent le code à tester.

Le développeur a ainsi besoin d’un nouvel outil mettant en œuvre le pattern de test "objet simulacre", c’est-à-dire un mock. Un mock est un objet qui va remplacer un objet réel (dans notre cas cela peut correspondre à une des dépendances du code que l’on veut tester), mais qui va retourner un résultat que le développeur va prédéfinir. Ainsi, le développeur n’a pas besoin de se soucier des dépendances sous-jacentes, et contrôle ainsi les données d’entrée pour le code qu’il veut tester.

Les mocks sont généralement utilisés dans les cas suivants :

  • Les dépendances dont le comportement ne peut pas être prédit à l’avance (relevé de température, date du jour…).

  • Les dépendances lourdes à manipuler (accès à la base).

  • Les dépendances possédant de fortes contraintes liées à l’environnement dans lequel s’exécute l’application (accès réseau, données GPS…).

  • Les dépendances de type interface utilisateur.

  • Les dépendances soumises à des mesures de métriques, comme par exemple le nombre de fois qu’une méthode a été appelée....

Les tests clients avec Jasmine et Karma

Le Web a bien changé depuis plusieurs années et tend à rendre les sites web de plus en plus interactifs, laissant de plus en plus de responsabilités au client plutôt qu’au serveur. Les exemples ci-dessous permettent d’apprécier les avantages de ce genre de procédé :

  • Chargement, ou rechargement partiel de la page (dans le cadre d’un menu).

  • Validation de formulaire côté client.

  • Récupération de données sans rafraîchissement de la page.

  • Allègement de la charge du serveur.

Toutes ces actions se font donc en JavaScript sur le client, mais cela peut rapidement devenir une usine à gaz si le code n’est pas un minimum organisé et architecturé. Dans le cas contraire, le navigateur peut ainsi avoir un comportement inexplicable, rendant le débogage difficile et fastidieux. Les tests sont un bon moyen de s’assurer que le code JavaScript effectue bien les actions demandées. 

Il n’a pas toujours été facile de tester du code JavaScript, mais avec les tendances actuelles, de nouveaux outils ont émergé pour répondre rapidement à ce besoin. La suite de cette partie traitera de l’un d’eux : Jasmine.js.

Avant toute chose, il est important de rappeler quelques notions importantes relatives à Jasmine :

  • Un runner est responsable de trouver et d’exécuter les tests. Chaque runner peut fonctionner différemment en proposant par exemple la catégorisation des tests, ou encore la parallélisation.

  • Un reporter permet simplement d’afficher les résultats émis par le runner.

Par défaut, Jasmine utilise un runner qui lance les tests et un reporter qui affiche les résultats en HTML. Prenons le code à tester ci-dessous qui convertit simplement des degrés Celsius en degrés Fahrenheit :

var MaLib = { 
    celsiusVersFahrenheit: function(celsius) { 
        if(isNaN(livres - 1)) { 
            throw "ValeurTempératureIncorrecte"; 
        } 
        return (celsius * 1.8) + 32; 
    } 
}; 

D’après le code...

L’alternative avec MSTest

Le framework MSTest (Microsoft Test) permet de créer, exécuter et gérer des tests unitaires pour du code .NET. Il est semblable au framework xUnit dans le sens où le développeur va pouvoir utiliser des attributs de méthode et de classe pour définir ses tests. La différence réside dans la grammaire des attributs, mais aussi dans le concept de base : xUnit a été conçu avec syntaxe minimaliste alors que MSTest a été conçu pour être compatible avec les outils de test existants, le rendant peut-être un peu moins facile à prendre en main pour les développeurs au premier abord.

Les attributs importants de MSTest sont les suivants :

  • [TestMethod] : cet attribut est utilisé pour décorer les méthodes de test. Chaque méthode sera alors un test qui sera exécuté pour MSTest.

  • [TestInitialize] : cet attribut est utilisé pour décorer une méthode qui sera exécutée avant chaque test. Ceci permet notamment d’initialiser des objets qui doivent être « propres » avant chaque exécution des tests.

  • [TestCleanup] : cet attribut est utilisé pour décorer une méthode qui sera exécutée après chaque test. Ceci permet notamment de nettoyer certains objets ou dépendances qui doivent être « propres » après chaque exécution de test.

  • [ClassInitialize] : cet attribut est utilisé pour décorer une méthode qui sera utilisée avant un ensemble de tests, plus particulièrement avant tous les tests de la classe courante. Ceci permet d’initialiser des objets et/ou des variables une seule fois avant tous les tests.

  • [ClassCleanup] : cet attribut est utilisé pour décorer une méthode...