Meilleures pratiques TUnit
Votre objectif est de vous aider à écrire des tests unitaires efficaces avec TUnit, couvrant à la fois les approches de test standard et data-driven.
Configuration du projet
- Utilisez un projet de test séparé avec la convention de nommage
[NomDuProjet].Tests - Référencez le package TUnit et TUnit.Assertions pour les assertions fluentes
- Créez des classes de test qui correspondent aux classes testées (par ex.
CalculatorTestspourCalculator) - Utilisez les commandes de test du SDK .NET :
dotnet testpour exécuter les tests - TUnit nécessite .NET 8.0 ou une version supérieure
Structure des tests
- Aucun attribut de classe de test requis (comme xUnit/NUnit)
- Utilisez l'attribut
[Test]pour les méthodes de test (pas[Fact]comme xUnit) - Suivez le modèle Arrange-Act-Assert (AAA)
- Nommez les tests selon le modèle
NomDeLaMéthode_Scénario_ComportementAttendu - Utilisez les hooks de cycle de vie :
[Before(Test)]pour la configuration et[After(Test)]pour le nettoyage - Utilisez
[Before(Class)]et[After(Class)]pour le contexte partagé entre les tests d'une classe - Utilisez
[Before(Assembly)]et[After(Assembly)]pour le contexte partagé entre les classes de test - TUnit prend en charge les hooks de cycle de vie avancés comme
[Before(TestSession)]et[After(TestSession)]
Tests standard
- Gardez les tests focalisés sur un seul comportement
- Évitez de tester plusieurs comportements dans une seule méthode de test
- Utilisez la syntaxe d'assertion fluente de TUnit avec
await Assert.That() - Incluez uniquement les assertions nécessaires pour vérifier le cas de test
- Rendez les tests indépendants et idempotents (peuvent s'exécuter dans n'importe quel ordre)
- Évitez les interdépendances entre tests (utilisez l'attribut
[DependsOn]si nécessaire)
Tests data-driven
- Utilisez l'attribut
[Arguments]pour les données de test inline (équivalent à[InlineData]de xUnit) - Utilisez
[MethodData]pour les données de test basées sur une méthode (équivalent à[MemberData]de xUnit) - Utilisez
[ClassData]pour les données de test basées sur une classe - Créez des sources de données personnalisées en implémentant
ITestDataSource - Utilisez des noms de paramètres significatifs dans les tests data-driven
- Plusieurs attributs
[Arguments]peuvent être appliqués à la même méthode de test
Assertions
- Utilisez
await Assert.That(value).IsEqualTo(expected)pour l'égalité des valeurs - Utilisez
await Assert.That(value).IsSameReferenceAs(expected)pour l'égalité des références - Utilisez
await Assert.That(value).IsTrue()ouawait Assert.That(value).IsFalse()pour les conditions booléennes - Utilisez
await Assert.That(collection).Contains(item)ouawait Assert.That(collection).DoesNotContain(item)pour les collections - Utilisez
await Assert.That(value).Matches(pattern)pour la correspondance avec une expression régulière - Utilisez
await Assert.That(action).Throws<TException>()ouawait Assert.That(asyncAction).ThrowsAsync<TException>()pour tester les exceptions - Enchaînez les assertions avec l'opérateur
.And:await Assert.That(value).IsNotNull().And.IsEqualTo(expected) - Utilisez l'opérateur
.Orpour les conditions alternatives :await Assert.That(value).IsEqualTo(1).Or.IsEqualTo(2) - Utilisez
.Within(tolerance)pour les comparaisons DateTime et numériques avec tolérance - Toutes les assertions sont asynchrones et doivent être attendues
Fonctionnalités avancées
- Utilisez
[Repeat(n)]pour répéter les tests plusieurs fois - Utilisez
[Retry(n)]pour les tentatives automatiques en cas d'échec - Utilisez
[ParallelLimit<T>]pour contrôler les limites d'exécution parallèle - Utilisez
[Skip("raison")]pour ignorer les tests de manière conditionnelle - Utilisez
[DependsOn(nameof(OtherTest))]pour créer des dépendances entre tests - Utilisez
[Timeout(millisecondes)]pour définir les délais d'expiration des tests - Créez des attributs personnalisés en étendant les attributs de base de TUnit
Organisation des tests
- Groupez les tests par fonctionnalité ou composant
- Utilisez
[Category("NomDeLaCatégorie")]pour la catégorisation des tests - Utilisez
[DisplayName("Nom du test personnalisé")]pour les noms de test personnalisés - Envisagez d'utiliser
TestContextpour les diagnostics et informations de test - Utilisez les attributs conditionnels comme
[WindowsOnly]personnalisé pour les tests spécifiques à une plateforme
Performance et exécution parallèle
- TUnit exécute les tests en parallèle par défaut (contrairement à xUnit qui nécessite une configuration explicite)
- Utilisez
[NotInParallel]pour désactiver l'exécution parallèle pour les tests spécifiques - Utilisez
[ParallelLimit<T>]avec des classes de limite personnalisées pour contrôler la concurrence - Les tests d'une même classe s'exécutent séquentiellement par défaut
- Utilisez
[Repeat(n)]avec[ParallelLimit<T>]pour les scénarios de test de charge
Migration depuis xUnit
- Remplacez
[Fact]par[Test] - Remplacez
[Theory]par[Test]et utilisez[Arguments]pour les données - Remplacez
[InlineData]par[Arguments] - Remplacez
[MemberData]par[MethodData] - Remplacez
Assert.Equalparawait Assert.That(actual).IsEqualTo(expected) - Remplacez
Assert.Trueparawait Assert.That(condition).IsTrue() - Remplacez
Assert.Throws<T>parawait Assert.That(action).Throws<T>() - Remplacez le constructeur/IDisposable par
[Before(Test)]/[After(Test)] - Remplacez
IClassFixture<T>par[Before(Class)]/[After(Class)]
Pourquoi TUnit plutôt que xUnit ?
TUnit offre une expérience de test moderne, rapide et flexible avec des fonctionnalités avancées absentes de xUnit, comme les assertions asynchrones, des hooks de cycle de vie plus affinés et des capacités de test data-driven améliorées. Les assertions fluentes de TUnit fournissent une validation de test plus claire et plus expressive, ce qui la rend particulièrement adaptée aux projets .NET complexes.