Cet article fournit une présentation du test unitaire de base de données d'une procédure stockée contenant une procédure utilitaire.
Dans cet article, je vais discuter d'un scénario de test unitaire de base de données lorsqu'une procédure stockée principale dépend d'une procédure utilitaire et que la procédure principale doit être testée unitaire afin de s'assurer que les exigences sont remplies. La clé est de s'assurer qu'un test unitaire ne peut être écrit que pour une seule unité de code, ce qui signifie que nous avons besoin d'un test unitaire pour la procédure principale et d'un autre test unitaire pour la procédure utilitaire.
Le test unitaire d'une procédure stockée unique est plus simple que le test unitaire d'une procédure qui appelle une procédure utilitaire dans son code.
Il est très important de comprendre le scénario de la procédure utilitaire et pourquoi il est différent du test unitaire d'une procédure stockée normale.
Scénario :procédure utilitaire dans la procédure principale
Afin de comprendre le scénario de procédure utilitaire, commençons par la définition et l'exemple de procédure utilitaire :
Qu'est-ce que la procédure utilitaire
Une procédure utilitaire est généralement une petite procédure qui est utilisée par la ou les procédures principales pour effectuer une tâche spécifique, comme obtenir quelque chose pour la procédure principale ou ajouter quelque chose à la procédure principale.
Une autre définition de procédure utilitaire est une petite procédure stockée écrite à des fins de maintenance qui peut impliquer des tables système ou des vues à appeler par n'importe quel nombre de procédures ou même directement.
Exemples de procédure utilitaire
Pensez à un scénario client-commande-produit où un client passe une commande pour un produit particulier. Si nous créons la procédure principale pour obtenir toutes les commandes passées par un client particulier, une procédure utilitaire peut être utilisée pour nous aider à comprendre si chaque commande a été passée par le client en semaine ou le week-end.
De cette façon, une Une petite procédure utilitaire peut être écrite pour renvoyer « Jour de la semaine » ou « Week-end » en fonction de la date à laquelle le produit a été commandé par le client.
Un autre exemple peut être les procédures stockées système telles que "sp_server_info" dans la base de données principale qui donne des informations sur la version installée de SQL Server :
EXEC sys.sp_server_info
Pourquoi la procédure de l'utilitaire de test unitaire est différente
Comme indiqué précédemment, le test unitaire d'une procédure utilitaire qui est appelée dans la procédure principale est un peu plus compliqué que le test unitaire d'une simple procédure stockée.
Considérant l'exemple client-commande-produit mentionné ci-dessus, nous devons écrire un test unitaire pour vérifier que la procédure utilitaire fonctionne correctement et également un test unitaire doit être écrit pour vérifier que la procédure principale qui appelle la procédure utilitaire fonctionne également correctement ainsi que la réunion le(s) besoin(s) métier.
Ceci est illustré comme suit :
Isolation de l'utilitaire/défi de procédure principale
Le principal défi dans l'écriture d'un ou plusieurs tests unitaires pour la procédure qui implique une procédure utilitaire est de s'assurer que nous ne devons pas nous soucier du fonctionnement de la procédure utilitaire lors de l'écriture d'un test unitaire pour la procédure principale et il en va de même pour la procédure utilitaire. . Il s'agit d'une tâche difficile qui doit être gardée à l'esprit lors de l'écriture de tests unitaires pour un tel scénario.
Il est indispensable de s'isoler de l'utilitaire ou de la procédure principale, en fonction de la procédure testée. Nous devons garder à l'esprit les éléments suivants dans le contexte de l'isolement lors des tests unitaires :
- Isolation de la procédure utilitaire lors du test unitaire de la procédure principale.
- Isolation de la procédure principale lors de la procédure de l'utilitaire de test unitaire.
N'oubliez pas que cet article se concentre sur les tests unitaires de la procédure principale en l'isolant de sa procédure utilitaire.
Création de la procédure principale et de sa procédure utilitaire
Afin d'écrire un test unitaire pour un scénario où la procédure utilitaire est utilisée par la procédure principale, nous devons d'abord avoir les prérequis suivants :
- Exemple de base de données
- Exigence(s) commerciale(s)
Configurer une base de données exemple (SQLBookShop)
Nous créons un exemple de base de données simple à deux tables appelée "SQLBookShop" qui contient les enregistrements de tous les livres commandés comme indiqué ci-dessous :
Créez un exemple de base de données SQLBookShop comme suit :
-- (1) Create SQLBookShop database CREATE DATABASE SQLBookShop; GO
Créez et remplissez les objets de base de données (tables) comme suit :
USE SQLBookShop; -- (2) Drop book and book order tables if they already exist IF EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES T WHERE T.TABLE_NAME='BookOrder') DROP TABLE dbo.BookOrder IF EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES T WHERE T.TABLE_NAME='Book') DROP TABLE dbo.Book IF EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES T WHERE T.TABLE_TYPE='View' AND t.TABLE_NAME='OrderedBooks') DROP VIEW dbo.OrderedBooks -- (3) Create book table CREATE TABLE Book (BookId INT PRIMARY KEY IDENTITY(1,1), Title VARCHAR(50), Stock INT, Price DECIMAL(10,2), Notes VARCHAR(200) ) -- (4) Create book order table CREATE TABLE dbo.BookOrder (OrderId INT PRIMARY KEY IDENTITY(1,1), OrderDate DATETIME2, BookId INT, Quantity INT, TotalPrice DECIMAL(10,2) ) -- (5) Adding foreign keys for author and article category ALTER TABLE dbo.BookOrder ADD CONSTRAINT FK_Book_BookId FOREIGN KEY (BookId) REFERENCES Book (BookId) -- (6) Populaing book table INSERT INTO dbo.Book (Title, Stock, Price, Notes) VALUES ('Mastering T-SQL in 30 Days', 10, 200, ''), ('SQL Database Reporting Fundamentals', 5, 100, ''), ('Common SQL Mistakes by Developers',15,100,''), ('Optimising SQL Queries',20,200,''), ('Database Development and Testing Tips',30,50,''), ('Test-Driven Database Development (TDDD)',20,200,'') -- (7) Populating book order table INSERT INTO dbo.BookOrder (OrderDate, BookId, Quantity, TotalPrice) VALUES ('2018-01-01', 1, 2, 400), ('2018-01-02', 2, 2, 200), ('2018-01-03', 3, 2, 200), ('2018-02-04', 1, 2, 400), ('2018-02-05', 1, 3, 600), ('2018-02-06', 4, 3, 600), ('2018-03-07', 5, 2, 100), ('2018-03-08', 6, 2, 400), ('2018-04-10', 5, 2, 100), ('2018-04-11', 6, 3, 600); GO -- (8) Creating database view to see all the books ordered by customers CREATE VIEW dbo.OrderedBooks AS SELECT bo.OrderId ,bo.OrderDate ,b.Title ,bo.Quantity ,bo.TotalPrice FROM BookOrder bo INNER JOIN Book b ON bo.BookId = b.BookId
Vérification rapide – Exemple de base de données
Effectuez une vérification rapide de la base de données en exécutant la vue OrderedBooks à l'aide du code suivant :
USE SQLBookShop -- Run OrderedBooks view SELECT ob.OrderID ,ob.OrderDate ,ob.Title AS BookTitle ,ob.Quantity ,ob.TotalPrice FROM dbo.OrderedBooks ob
Veuillez noter que j'utilise dbForge Studio pour SQL Server, donc l'aspect de la sortie peut différer si vous exécutez le même code dans SSMS (SQL Server Management Studio). Cependant, il n'y a aucune différence entre les scripts et leurs résultats.
Exigence commerciale pour voir la dernière commande avec des informations supplémentaires
Une exigence commerciale a été envoyée à l'équipe de développement indiquant que "l'utilisateur final souhaite connaître la commande la plus récente passée pour un livre particulier, ainsi que les informations indiquant si la commande a été passée un jour de semaine ou un week-end"
Un mot sur TDDD
Nous ne suivons pas strictement le développement de base de données piloté par les tests (TDDD) dans cet article, mais je recommande fortement d'utiliser le développement de base de données piloté par les tests (TDDD) pour créer à la fois des procédures principales et utilitaires qui commencent par créer un test unitaire pour vérifier si l'objet existe qui échoue dans un premier temps, suivi de la création de l'objet et de la réexécution du test unitaire qui doit réussir.
Pour une présentation détaillée, veuillez vous reporter à la première partie de cet article.
Procédure d'identification de l'utilitaire
En voyant les besoins de l'entreprise, une chose est sûre, nous avons besoin d'une procédure utilitaire qui peut nous dire si une date particulière est un jour de semaine ou un week-end.
Création d'une procédure utilitaire (GetDayType)
Créez une procédure utilitaire et appelez-la "GetDayType" comme suit :
-- Creating utility procedure to check whether the date passed to it is a weekday or weekend CREATE PROCEDURE dbo.uspGetDayType @OrderDate DATETIME2,@DayType CHAR(7) OUT AS BEGIN SET NOCOUNT ON IF (SELECT DATENAME(WEEKDAY, @OrderDate)) = 'Saturday' OR (SELECT DATENAME(WEEKDAY, @OrderDate)) = 'Sunday' SELECT @DayType= 'Weekend' ELSE SELECT @DayType = 'Weekday' SET NOCOUNT OFF END GO
Vérification rapide - Procédure utilitaire
Écrivez les lignes de code suivantes pour vérifier rapidement la procédure de l'utilitaire :
-- Quick check utility procedure declare @DayType varchar(10) EXEC uspGetDayType '20181001',@DayType output select @DayType AS [Type of Day]
Création de la procédure principale (GetLatestOrderByBookId)
Créez la procédure principale pour voir la commande la plus récente passée pour un livre particulier et également si la commande a été passée un jour de semaine ou un week-end et appelez-la "GetLatestOrderByBookId" qui contient l'appel de la procédure utilitaire comme suit :
-- Creating stored procedure to get most recent order based on bookid and also whether order was placed on weekend or weekday CREATE PROCEDURE dbo.uspGetLatestOrderByBookId @BookId INT AS BEGIN -- Declare variables to store values DECLARE @OrderId INT ,@Book VARCHAR(50) ,@OrderDate DATETIME2 ,@Quantity INT ,@TotalPrice DECIMAL(10, 2) ,@DayType VARCHAR(10) -- Get most recent order for a particular book and initialise variables SELECT TOP 1 @OrderId = bo.OrderId ,@Book = b.Title ,@OrderDate = bo.OrderDate ,@Quantity = bo.Quantity ,@TotalPrice = bo.TotalPrice FROM BookOrder bo INNER JOIN Book b ON bo.BookId = b.BookId WHERE bo.BookId = @BookId ORDER BY OrderDate DESC -- Call utility procedure to get type of day for the above selected most recent order EXEC uspGetDayType @OrderDate ,@DayType OUTPUT -- Show most recent order for a particular book along with the information whether order was placed on weekday or weekend SELECT @OrderId AS OrderId ,@OrderDate AS OrderDate ,@Book AS Book ,@Quantity AS Quantity ,@TotalPrice AS TotalPrice ,@DayType AS DayType END GO
Vérification rapide - Procédure principale
Exécutez le code suivant pour voir si la procédure fonctionne correctement ou non :
-- Get latest order for the bookid=6 EXEC uspGetLatestOrderByBookId @BookId = 6
Procédure principale de test unitaire Procédure d'appel de l'utilitaire
La clé ici est de comprendre la différence entre les tests unitaires de la procédure principale et la procédure utilitaire.
Nous nous concentrons actuellement sur les tests unitaires de la procédure principale, ce qui signifie que la procédure utilitaire doit être soigneusement isolée de ce test unitaire.
Utilisation de la procédure d'espionnage
Afin de s'assurer que le test unitaire de la procédure principale reste concentré sur le test de la fonctionnalité de la procédure principale, nous devons utiliser la procédure d'espionnage fournie par tSQLt qui va agir comme un stub (espace réservé) pour la procédure utilitaire.
Selon tsqlt.org, rappelez-vous que si vous espionnez une procédure, vous ne testez pas réellement cette procédure, mais vous facilitez le test unitaire de l'autre procédure liée à la procédure que vous espionnez.
Par exemple, dans notre cas, si nous voulons tester unitairement la procédure principale, nous devons nous moquer de la procédure utilitaire en utilisant une procédure d'espionnage qui nous facilitera le test unitaire de la procédure principale.
Création d'un test unitaire pour la procédure principale de l'utilitaire d'espionnage
Créez un test unitaire de base de données pour vérifier correctement les fonctions de la procédure principale.
Cet article fonctionne pour dbForge Studio for SQL Server (ou uniquement dbForge Unit Test) et SSMS (SQL Server Management Studio) . Cependant, veuillez noter que lorsque vous utilisez SSMS (SQL Server Management Studio), je suppose que vous avez déjà installé tSQLt Framework et que vous êtes prêt à écrire les tests unitaires.
Pour créer le premier test unitaire de base de données, cliquez avec le bouton droit sur la base de données SQLBookShop. Dans le menu contextuel, cliquez sur Test unitaire puis sur Ajouter un nouveau test comme suit :
Écrivez le code du test unitaire :
CREATE PROCEDURE GetLatestOrder.[test to check uspGetLatestOrderByBookId outputs correct data] AS BEGIN --Assemble -- Mock order Book and BookOrder table EXEC tSQLt.FakeTable @TableName='dbo.Book' EXEC tSQLt.FakeTable @TableName='dbo.BookOrder' -- Adding mock data to book table INSERT INTO dbo.Book (BookId,Title, Stock, Price, Notes) VALUES (1,'Basics of T-SQL Programming', 10, 100, ''), (2,'Advanced T-SQL Programming', 10, 200, '') -- Adding mock data to bookorder table INSERT INTO dbo.BookOrder (OrderId,OrderDate, BookId, Quantity, TotalPrice) VALUES (1,'2018-01-01', 1, 2, 200), (2,'2018-05-01', 1, 2, 200), (3,'2018-07-01', 2, 2, 400) -- Creating expected table CREATE TABLE GetLatestOrder.Expected ( OrderId INT ,OrderDate DATETIME2 ,Book VARCHAR(50) ,Quantity INT ,TotalPrice DECIMAL(10, 2) ,DayType VARCHAR(10) ) -- Creating actual table CREATE TABLE GetLatestOrder.Actual ( OrderId INT ,OrderDate DATETIME2 ,Book VARCHAR(50) ,Quantity INT ,TotalPrice DECIMAL(10, 2) ,DayType VARCHAR(10) ) -- Creating uspGetDayType spy procedure to isolate main procedure from it so that main procedure can be unit tested EXEC tSQLt.SpyProcedure @ProcedureName = 'dbo.uspGetDayType',@CommandToExecute = 'set @DayType = ''Weekday'' ' -- Inserting expected values to the expected table INSERT INTO GetLatestOrder.Expected (OrderId, OrderDate, Book, Quantity, TotalPrice, DayType) VALUES (2,'2018-05-01', 'Basics of T-SQL Programming', 2, 200,'Weekday'); --Act INSERT INTO GetLatestOrder.Actual EXEC uspGetLatestOrderByBookId @BookId = 1 -- Calling the main procedure --Assert --Compare expected results with actual table results EXEC tSQLt.AssertEqualsTable @Expected = N'GetLatestOrder.Expected', -- nvarchar(max) @Actual = N'GetLatestOrder.Actual' -- nvarchar(max) END; GO
Exécution du test unitaire pour la procédure principale
Exécutez le test unitaire :
Félicitations, vous avez réussi le test unitaire d'une procédure stockée en l'isolant de sa procédure utilitaire après avoir utilisé la procédure d'espionnage.
Pour plus d'informations sur les tests unitaires, veuillez consulter les parties suivantes de mon article précédent sur le développement de bases de données pilotées par les tests (TDDD) :
- Démarrer le développement de bases de données pilotées par les tests (TDDD) – Partie 1
- Démarrer le développement de bases de données pilotées par les tests (TDDD) – Partie 2
- Démarrer le développement de bases de données pilotées par les tests (TDDD) – Partie 3
Choses à faire
Vous pouvez désormais créer des tests unitaires de base de données pour des scénarios légèrement complexes dans lesquels des procédures stockées appellent des procédures utilitaires.
- Veuillez essayer de changer l'argument (valeur) de la procédure d'espionnage @CommandToExecute en tant que @CommandToExecute ='set @DayType =”Nothing” ' et voyez que le test va échouer maintenant
- Veuillez essayer de répondre aux exigences commerciales de cet article en utilisant le développement de base de données piloté par les tests (TDDD)
- Veuillez essayer de répondre à une autre exigence commerciale pour voir la commande la plus récente passée par un client utilisant le développement piloté par les tests (TDDD) impliquant la même procédure utilitaire
- Veuillez essayer de créer un test unitaire pour la procédure utilitaire en isolant la procédure principale
- Veuillez essayer de créer un test unitaire pour une procédure qui appelle deux procédures utilitaires
Outil utile :
dbForge Unit Test - une interface graphique intuitive et pratique pour la mise en œuvre de tests unitaires automatisés dans SQL Server Management Studio.