Tests unitaires des bibliothèques F# à l’aide de dotnet test et TUnit

Ce tutoriel vous guide tout au long d’une expérience interactive qui crée un exemple de solution pas à pas pour apprendre les concepts de test unitaire.

Cet article concerne le test d’un projet .NET Core. Si vous testez un projet ASP.NET Core, consultez tests d’intégration dans ASP.NET Core.

Prerequisites

  • .NET 10 SDK ou versions ultérieures.
  • Un éditeur de texte ou un éditeur de code de votre choix.

TUnit est entièrement basé sur Microsoft. Testing.Platform (MTP) et ne prend pas en charge VSTest. Ce tutoriel utilise le mode MTP de dotnet test, qui nécessite le sdk .NET 10.

Créer le projet source

Ouvrez une fenêtre shell. Créez un répertoire appelé unit-testing-with-fsharp pour contenir la solution. Dans ce nouveau répertoire, exécutez la commande suivante pour créer un fichier solution pour la bibliothèque de classes et le projet de test :

dotnet new sln

Ensuite, créez un répertoire MathService . Le plan suivant montre la structure de répertoire et de fichier jusqu’à présent :

/unit-testing-with-fsharp
    unit-testing-with-fsharp.slnx
    /MathService

Faites de MathService le répertoire actif et exécutez la commande suivante pour créer le projet source :

dotnet new classlib -lang "F#"

Remplacez le contenu de Library.fs par une implémentation défaillante du service mathématique :

namespace MathService

module MyMath =
    let squaresOfOdds (xs: int seq) : int seq = raise (System.NotImplementedException("You haven't written a test yet!"))

Revenez au répertoire unit-testing-with-fsharp . Exécutez la commande suivante pour ajouter le projet de bibliothèque de classes à la solution :

dotnet sln add ./MathService/MathService.fsproj

Créer le projet de test

Ensuite, créez le répertoire MathService.Tests . Le plan suivant montre la structure de répertoires :

/unit-testing-with-fsharp
    unit-testing-with-fsharp.slnx
    /MathService
        Source Files
        MathService.fsproj
    /MathService.Tests

Les modèles de projet TUnit ne sont pas inclus dans le Kit de développement logiciel (SDK) .NET. Installez-les avec la commande suivante :

dotnet new install TUnit.Templates

Créez le répertoire MathService.Tests dans le répertoire actif et créez un projet à l’aide de la commande suivante :

dotnet new TUnit -lang "F#"

Note

Les noms courts de modèle respectent la casse. Utilisez TUnit, pas tunit.

Cette commande crée un projet de test qui utilise TUnit comme framework de test. Le fichier MathService.Tests.fsproj généré est un projet exécutable qui fait référence au TUnit package, qui inclut l’infrastructure de test, la bibliothèque d’assertions et l’exécuteur de test MTP, et le TUnit.Assertions.FSharp package, qui ajoute des helpers F# pour les assertions.

Le modèle ajoute également des exemples de tests. Ce tutoriel remplace ces tests par ses propres tests dans Tests.fs. Supprimez le répertoire Data et les fichiers GlobalSetup.fs, Tests2.fs et Tests3.fs. Les projets F# répertorient explicitement leurs fichiers sources, donc remplacez aussi le ItemGroup qui contient les éléments Compile dans MathService.Tests.fsproj par les éléments suivants :

<ItemGroup>
  <Compile Include="Tests.fs" />
</ItemGroup>

À présent, ajoutez la bibliothèque de classes MathService comme autre dépendance au projet. Utiliser la commande dotnet reference add :

dotnet reference add ../MathService/MathService.fsproj

Le plan suivant montre la disposition finale de la solution :

/unit-testing-with-fsharp
    unit-testing-with-fsharp.slnx
    /MathService
        Source Files
        MathService.fsproj
    /MathService.Tests
        Test Source Files
        MathService.Tests.fsproj

Exécutez la commande suivante dans le répertoire unit-testing-with-fsharp :

dotnet sln add ./MathService.Tests/MathService.Tests.fsproj

Configurer le mode MTP de dotnet test

Étant donné que TUnit ne prend pas en charge VSTest, dotnet test doit s’exécuter en mode MTP. Dans le répertoire unit-testing-with-fsharp , créez un fichier global.json avec le contenu suivant :

{
    "test": {
        "runner": "Microsoft.Testing.Platform"
    }
}

Pour plus d’informations, consultez le mode MTP du test dotnet.

Créer le premier test

Vous écrivez un test défaillant, faites-le passer, puis répétez le processus. Remplacez le contenu de Tests.fs par le code suivant :

namespace MathService.Tests

open TUnit.Core
open TUnit.Assertions
open TUnit.Assertions.Extensions
open TUnit.Assertions.FSharp.TaskAssert
open MathService

type TestClass () =

    [<Test>]
    member this.TestMethodPassing() = taskAssert {
        do! Assert.That(true).IsTrue()
    }

    [<Test>]
    member this.FailEveryTime() = taskAssert {
        do! Assert.That(false).IsTrue()
    }

L’attribut [<Test>] désigne une méthode de test exécutée par l’exécuteur de test. TUnit découvre les méthodes de test qui appartiennent à une classe ; TUnit ne découvre pas les fonctions déclarées let dans un module. Les assertions TUnit sont asynchrones. L’expression de calcul taskAssert provenant de TUnit.Assertions.FSharp exécute un test en tant que tâche et vous permet d’attendre chaque assertion avec do!.

À partir du répertoire unit-testing-with-fsharp , exécutez dotnet test pour générer les tests et la bibliothèque de classes, puis exécutez les tests. Le projet de test TUnit contient le point d’entrée du programme pour exécuter vos tests. dotnet test démarre l’application de test à l’aide du projet de test unitaire que vous avez créé.

Ces deux tests montrent les tests de réussite et d’échec les plus élémentaires. TestMethodPassing passe et FailEveryTime échoue. À présent, créez un test pour la squaresOfOdds méthode. La squaresOfOdds méthode retourne une séquence des carrés de toutes les valeurs entières impaires qui font partie de la séquence d’entrée. Au lieu d’essayer d’écrire toutes ces fonctions à la fois, vous pouvez créer des tests itératifs qui valident la fonctionnalité. Pour passer chaque test, vous créez les fonctionnalités nécessaires dans la méthode.

Le test le plus simple que vous pouvez écrire consiste à appeler squaresOfOdds avec tous les nombres pairs, où le résultat doit être une séquence vide d’entiers. Ajoutez le test suivant au type TestClass :

[<Test>]
member this.TestEvenSequence() = taskAssert {
    let expected = Seq.empty<int>
    let actual = MyMath.squaresOfOdds [2; 4; 6; 8; 10]
    do! Assert.That<int>(actual).IsEquivalentTo(expected)
}

F# est plus strict que C# lorsqu’il résout les surcharges de méthode, de sorte que le test passe le type d’élément à Assert.That<int> pour sélectionner les assertions pour une séquence d’entiers. IsEquivalentTo vérifie que les deux séquences contiennent les mêmes éléments.

Lorsque vous exécutez le test, vous voyez que votre test échoue. C’est parce que vous n’avez pas encore créé l’implémentation. Faites réussir ce test en écrivant le code le plus simple dans le fichier Library.fs de votre projet MathService qui fonctionne :

let squaresOfOdds xs =
    Seq.empty<int>

Dans le répertoire unit-testing-with-fsharp , réexécutez dotnet test . La commande dotnet test exécute une build pour le projet MathService, puis pour le projet MathService.Tests. Après avoir généré les deux projets, il exécute vos tests. À présent, les deux tests réussissent.

Remplir les conditions requises

Maintenant que vous avez passé un test, il est temps d’écrire plus. Le cas simple suivant fonctionne avec une séquence dont le seul nombre impair est 1. Le nombre 1 est plus facile, car le carré de 1 est 1. Voici ce prochain test :

[<Test>]
member public this.TestOnesAndEvens() = taskAssert {
    let expected = [1; 1; 1; 1]
    let actual = MyMath.squaresOfOdds [2; 1; 4; 1; 6; 1; 8; 1; 10]
    do! Assert.That<int>(actual).IsEquivalentTo(expected)
}

L’exécution dotnet test fait échouer le nouveau test. Vous devez mettre à jour la squaresOfOdds méthode pour gérer ce nouveau test. Vous devez filtrer tous les nombres pairs hors de la séquence pour que ce test réussisse. Vous pouvez le faire en écrivant une petite fonction de filtre et en utilisant Seq.filter:

let private isOdd x = x % 2 <> 0

let squaresOfOdds xs =
    xs
    |> Seq.filter isOdd

Encore une étape : calculer le carré de chaque nombre impair. Commencez par écrire un nouveau test :

[<Test>]
member public this.TestSquaresOfOdds() = taskAssert {
    let expected = [1; 9; 25; 49; 81]
    let actual = MyMath.squaresOfOdds [1; 2; 3; 4; 5; 6; 7; 8; 9; 10]
    do! Assert.That<int>(actual).IsEquivalentTo(expected)
}

Vous pouvez corriger le test en redirigeant la séquence filtrée via une opération de mappage pour calculer le carré de chaque nombre impair :

let private square x = x * x
let private isOdd x = x % 2 <> 0

let squaresOfOdds xs =
    xs
    |> Seq.filter isOdd
    |> Seq.map square

Vous avez créé une petite bibliothèque et un ensemble de tests unitaires pour cette bibliothèque. Vous avez structuré la solution afin que l’ajout de nouveaux packages et tests fasse partie du flux de travail normal. Vous avez concentré la plupart de votre temps et votre effort sur la résolution des objectifs de l’application.

Voir aussi