Concevoir et implémenter une stratégie de test
Un projet de base de données SQL qui s’génère correctement ne signifie pas que les procédures stockées à l’intérieur de celle-ci retournent les résultats appropriés. Une procédure peut être compilée sans erreur et calculer les totaux incorrects ou ignorer une vérification de validation. Les tests interceptent ces problèmes avant qu’ils atteignent la production.
Comprendre les niveaux de test de base de données
Les tests de base de données fonctionnent dans des couches, chacun interceptant une classe de problème différente :
-
La validation du build est l’étape
dotnet buildque vous avez déjà. Il intercepte les erreurs de syntaxe et les références d’objets rompues. Rapide, mais il prouve que le schéma est structurellement valide. - Les tests unitaires vérifient que les procédures stockées et les fonctions individuelles produisent les résultats appropriés pour un ensemble d’entrées donné. Ils interceptent les erreurs logiques.
- Les tests d’intégration effectuent des scénarios de bout en bout sur une base de données déployée. Ils prouvent que les objets fonctionnent correctement, mais ils nécessitent une instance en cours d’exécution et prennent le plus de temps.
Chaque couche est plus lente et plus coûteuse que celle qui l’a précédée, mais détecte également les problèmes que la couche précédente ne peut pas rencontrer.
Créer des tests unitaires SQL Server
SQL Server Data Tools (SSDT) dans Visual Studio inclut une infrastructure intégrée pour les tests unitaires de base de données. Chaque test exécute T-SQL sur une base de données dynamique et valide les résultats à l’aide de conditions de test.
Structure d’un test unitaire
Chaque test comporte trois sections qui suivent un modèle setup-execute-cleanup :
- Pré-test : configurez les données dont le test a besoin. Insérez des enregistrements client, effacez les données restantes des exécutions précédentes.
- Test : exécutez l’opération que vous testez. Appelez la procédure stockée et interrogez la vue.
- Après le test : nettoyer après le test pour qu'il ne contamine pas le prochain cycle.
Conditions de test
Après l’exécution du test T-SQL, les conditions de test valident ce qui est revenu. Les conditions les plus couramment utilisées sont les suivantes :
- Nombre de lignes : vérifie que le jeu de résultats contient le nombre attendu de lignes.
- Valeur scalaire : vérifie qu’une cellule spécifique dans le jeu de résultats contient la valeur attendue.
- Schéma attendu : vérifie que le jeu de résultats a les noms de colonnes et les types de données attendus.
D’autres conditions intégrées incluent la somme de contrôle des données, le jeu de résultats vide, le jeu de résultats non vide et le temps d’exécution.
Créer un projet de test unitaire
La configuration des tests unitaires SQL Server dans Visual Studio effectue quelques étapes :
- Ouvrez votre projet de base de données SQL.
- Dans l’Explorateur d’objets SQL Server, recherchez la procédure stockée ou la fonction que vous souhaitez tester.
- Cliquez avec le bouton droit sur l’objet, puis sélectionnez Créer des tests unitaires.
- Choisissez ou créez un projet de test C#.
- Définissez la connexion de test à votre base de données de développement.
- Sélectionnez Déployer automatiquement le projet de base de données avant l’exécution des tests unitaires. Cette option conserve la base de données de test synchronisée avec les dernières modifications de votre projet.
Le concepteur s’ouvre avec un modèle T-SQL dans lequel vous écrivez votre logique de test et attachez des conditions de test.
Écrire des tests unitaires de base de données effectifs
Un bon test unitaire répond à une question spécifique : « Cette procédure fait-elle ce qu’elle doit faire pour une entrée connue ? » Voici un exemple qui teste uspPlaceNewOrder, en vérifiant que le passage d’une commande met correctement à jour le total annuel du client :
-- Pre-test: Set up customer and clear previous data
DECLARE @CustomerID INT;
INSERT INTO [Sales].[Customer] (CustomerName) VALUES (N'Test Customer');
SET @CustomerID = SCOPE_IDENTITY();
DELETE FROM [Sales].[Orders] WHERE [CustomerID] = @CustomerID;
-- Test: Place an order and verify YTDOrders updated correctly
DECLARE @RC INT;
EXECUTE @RC = [Sales].[uspPlaceNewOrder] @CustomerID, 100, GETDATE(), 'O';
SELECT [YTDOrders]
FROM [Sales].[Customer]
WHERE [CustomerID] = @CustomerID;
📝 Associez ce test T-SQL à une condition de test Scalar Value qui s’attend à ce que la YTDOrders valeur soit 100.
Tests négatifs
Vous devez également vérifier que les procédures stockées échouent correctement lorsqu’elles reçoivent des entrées non valides. Par exemple, l’annulation d’une commande déjà expédiée doit déclencher une erreur, et ne pas se faire discrètement. Un test négatif confirme que l’erreur attendue se produit.
Lorsque vous sélectionnez Créer des tests unitaires à l’étape 3 de la section précédente, Visual Studio génère un projet de test C# dans l’Explorateur de solutions avec un fichier contenant un .cs test pour chaque procédure stockée sélectionnée. Par exemple, si vous avez créé un test pour uspCancelOrder, le fichier généré inclut une section nommée Sales_uspCancelOrder_Test. Pour marquer ce test comme test négatif, ouvrez le .cs fichier dans l’Explorateur de solutions et ajoutez l’attribut ExpectedSqlException directement au-dessus de celui-ci et enregistrez le fichier :
[TestMethod()]
[ExpectedSqlException(Severity = 16, MatchFirstError = false, State = 1)]
public void Sales_uspCancelOrder_FilledOrder_Test()
Le test réussit uniquement lorsque la procédure stockée génère une erreur correspondant à la gravité et à l’état spécifiés. Si la procédure réussit silencieusement, le test échoue, ce qui est exactement ce que vous voulez.
Concevoir une approche de test d’intégration
Les tests unitaires isolent des objets individuels. Les tests d’intégration vont plus loin et testent des scénarios qui s’étendent sur plusieurs opérations. Ils répondent à des questions telles que :
- Une séquence d’appels de procédure stockée laisse-t-elle la base de données dans l’état attendu ?
- Les déclencheurs se déclenchent-ils correctement lorsque les données arrivent par le biais de la couche Application ?
- Les vues retournent-elles des résultats précis après une série de modifications de table associées ?
Les tests d’intégration ont besoin d’une base de données dédiée. Déployez le projet de base de données SQL sur une instance de test avant chaque exécution à l'aide du paramètre Déployer automatiquement le projet de base de données avant l’exécution des tests unitaires. Ce paramètre conserve le schéma de test actif.
Considérations relatives aux bases de données de test
Les tests unitaires et les tests d’intégration exécutent T-SQL sur une base de données dynamique. L’environnement de test a donc besoin d’une configuration minutieuse pour produire des résultats fiables.
- Isolez les tests de la production. Utilisez une instance distincte ou une base de données de test dédiée. L’exécution de tests sur la production n’est jamais recommandée.
- Réinitialisez l’état connu avant chaque exécution. Les scripts post-déploiement ou les scripts de nettoyage de test gèrent ce problème.
- Externalisez les chaînes de connexion dans les fichiers de configuration afin que le développement local et les pipelines CI pointent vers la base de données appropriée sans modification du code.
Intégrer des tests dans des pipelines CI/CD
Ajoutez une étape de test après la génération et le déploiement afin que les tests s’exécutent automatiquement sur chaque validation. Dans Azure DevOps, utilisez la VSTest tâche :
- task: VSTest@2
inputs:
testAssembly: '**\*Tests.dll'
searchFolder: '$(System.DefaultWorkingDirectory)'
Dans GitHub Actions, exécutez les tests à l’aide de dotnet test:
- name: Run database unit tests
run: dotnet test ./DatabaseTests/DatabaseTests.csproj
Conseil / Astuce
Configurez le projet de test pour déployer automatiquement le projet de base de données avant l’exécution des tests. Ce paramètre garantit que le schéma de base de données de test correspond au projet au moment du test.
Lorsqu’un test échoue, le pipeline s’arrête et la modification n’atteint pas la préproduction ou la production. Ce délai permet à l’équipe de résoudre le problème avant qu’il n’affecte n’importe quel environnement.
Points clés à prendre
Les tests de base de données s’étendent sur trois niveaux : validation de build pour la correction structurelle, tests unitaires pour la vérification logique et tests d’intégration pour les flux de travail de bout en bout. Les tests unitaires SQL Server suivent une structure en trois phases de préactions de test, d’actions de test et de post-actions de test. Pour vérifier le comportement de procédure stockée et de fonction, utilisez des conditions de test telles que Scalar Value, Row Countet Expected Schema. Pour tester les méthodes qui doivent valider les chemins de gestion des erreurs, ajoutez l’attribut ExpectedSqlException . Filez les projets de test dans votre pipeline CI/CD afin qu’un test défaillant bloque le déploiement avant que les modifications atteignent la préproduction ou la production. Ensemble, ces trois couches forment un filet de sécurité qui permet à votre équipe de déployer des modifications de base de données avec confiance au lieu de l’anxiété.