Den här artikeln ger en genomgång av en databasenhet som testar en lagrad procedur som innehåller en verktygsprocedur i den.
I den här artikeln kommer jag att diskutera ett scenario för databasenhetstestning när en huvudsaklig lagrad procedur beror på en verktygsprocedur och huvudproceduren måste enhetstestades för att säkerställa att kraven uppfylls. Nyckeln är att säkerställa att ett enhetstest endast kan skrivas för en enda kodenhet, vilket innebär att vi behöver ett enhetstest för huvudproceduren och ett annat enhetstest för verktygsproceduren.
Enhetstestning av en enskild lagrad procedur är enklare jämfört med att enhetstesta en procedur som anropar en verktygsprocedur i sin kod.
Det är mycket viktigt att förstå scenariot med verktygsproceduren och varför det är annorlunda än att enhetstesta en normal lagrad procedur.
Scenario:Verktygsprocedur inom huvudprocedur
För att förstå scenariot med verktygsprocedur låt oss börja med definitionen och exemplet på verktygsprocedur:
Vad är Utility Procedur
En hjälpprocedur är i allmänhet en liten procedur som används av huvudprocedurerna för att utföra någon specifik uppgift som att få något för huvudproceduren eller lägga till något till huvudproceduren.
En annan definition av verktygsprocedur är en liten lagrad procedur skriven för underhållsändamål som kan involvera systemtabeller eller vyer som ska anropas av ett valfritt antal procedurer eller till och med direkt.
Exempel på verktygsprocedur
Tänk på ett kund-order-produkt scenario där en kund gör en beställning för en viss produkt. Om vi skapar huvudproceduren för att få oss alla beställningar som lagts av en viss kund, kan en hjälpprocedur användas för att hjälpa oss att förstå om varje beställning gjordes av kunden på vardagar eller helger.
På detta sätt kan en liten verktygsprocedur kan skrivas för att returnera "Weekday" eller "Weekend" baserat på det datum produkten beställdes av kunden.
Ett annat exempel kan vara systemlagrade procedurer som "sp_server_info" i huvuddatabasen som ger SQL Server installerad versionsinformation:
EXEC sys.sp_server_info
Varför förfarandet för enhetstestning är annorlunda
Som diskuterats tidigare är enhetstestning av en verktygsprocedur som anropas i huvudproceduren något komplicerad än enhetstestning av en enkel lagrad procedur.
Med tanke på kund-order-produktexemplet som nämnts ovan, måste vi skriva ett enhetstest för att kontrollera att verktygsproceduren fungerar bra och även ett enhetstest måste skrivas för att kontrollera att huvudproceduren som anropar verktygsproceduren också fungerar korrekt samt möter affärskraven.
Detta illustreras enligt följande:
Isolera från Utility/Main Procedure Challenge
Den största utmaningen med att skriva ett enhetstest(er) för proceduren som involverar ett verktygsförfarande är att se till att vi inte ska oroa oss för hur verktygsförfarandet fungerar när vi skriver ett enhetstest för huvudproceduren och detsamma gäller för verktygsförfarandet. . Detta är en utmanande uppgift som måste hållas i åtanke när du skriver enhetstester för ett sådant scenario.
Isolering från verktyget eller huvudproceduren är ett måste, beroende på vilken procedur som testas. Vi måste ha följande saker i åtanke i samband med isolering under enhetstestning:
- Isolera från verktygsproceduren när huvudproceduren testas av enheten.
- Isolering från huvudproceduren vid enhetstestning av verktygsprocedur.
Kom ihåg att den här artikeln fokuserar på enhetstestning av huvudproceduren genom att isolera den från dess verktygsprocedur.
Skapa huvudprocedur och dess verktygsprocedur
För att kunna skriva ett enhetstest för ett scenario där verktygsproceduren används av huvudproceduren måste vi först ha följande förutsättningar:
- Exempeldatabas
- Företagskrav
Setup Sample Database (SQLBookShop)
Vi skapar en enkel exempeldatabas med två tabeller som heter "SQLBookShop" som innehåller register över alla böcker som beställts enligt nedan:
Skapa SQLBookShop exempeldatabas enligt följande:
-- (1) Create SQLBookShop database CREATE DATABASE SQLBookShop; GO
Skapa och fyll i databasobjekt (tabeller) enligt följande:
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
Snabbkontroll – exempeldatabas
Gör en snabb databaskontroll genom att köra vyn OrderedBooks med följande kod:
USE SQLBookShop -- Run OrderedBooks view SELECT ob.OrderID ,ob.OrderDate ,ob.Title AS BookTitle ,ob.Quantity ,ob.TotalPrice FROM dbo.OrderedBooks ob
Observera att jag använder dbForge Studio för SQL Server så utdatautseendet kan skilja sig om du kör samma kod i SSMS (SQL Server Management Studio). Det finns dock ingen skillnad mellan skript och deras resultat.
Företagskrav för att se den senaste beställningen med ytterligare information
Ett affärskrav har skickats till utvecklarteamet som säger att "Slutanvändaren vill veta om den senaste beställningen för en viss bok tillsammans med informationen om beställningen gjordes på en vardag eller helg"
Ett ord om TDDD
Vi följer inte strikt testdriven databasutveckling (TDDD) i den här artikeln men jag rekommenderar starkt att använda testdriven databasutveckling (TDDD) för att skapa både huvud- och verktygsprocedurer som börjar med att skapa ett enhetstest för att kontrollera om objektet existerar som misslyckas först, följt av att skapa objektet och köra enhetstestet igen som måste godkännas.
För en detaljerad genomgång, se den första delen av den här artikeln.
Procedur för att identifiera verktyg
När vi ser affärskravet är en sak säker att vi behöver ett verktygsprocedur som kan berätta för oss om ett visst datum är en vardag eller en helg.
Skapa verktygsprocedur (GetDayType)
Skapa en verktygsprocedur och kalla den "GetDayType" enligt följande:
-- 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
Snabbkontroll – Utility Procedur
Skriv följande kodrader för att snabbt kontrollera verktygsproceduren:
-- Quick check utility procedure declare @DayType varchar(10) EXEC uspGetDayType '20181001',@DayType output select @DayType AS [Type of Day]
Skapa huvudprocedur (GetLatestOrderByBookId)
Skapa huvudproceduren för att se den senast gjorda beställningen för en viss bok och även om beställningen gjordes på en veckodag eller helg och kalla den "GetLatestOrderByBookId" som innehåller anropet för verktygsproceduren enligt följande:
-- 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
Snabbkontroll – Huvudprocedur
Kör följande kod för att se om proceduren fungerar bra eller inte:
-- Get latest order for the bookid=6 EXEC uspGetLatestOrderByBookId @BookId = 6
Huvudprocedur för enhetstestning Anropsverktygsprocedur
Nyckeln här är att förstå skillnaden mellan enhetstestning av huvudproceduren och verktygsproceduren.
Vi är för närvarande fokuserade på att enhetstesta huvudproceduren, så det betyder att verktygsproceduren måste isoleras på ett elegant sätt från detta enhetsteste.
Användning av spionprocedur
För att säkerställa att huvudprocedurens enhetstest förblir fokuserad på att testa funktionaliteten hos huvudproceduren, måste vi använda spionproceduren tillhandahållen av tSQLt som kommer att fungera som en stubb (platshållare) för verktygsproceduren.
Enligt tsqlt.org, kom ihåg att om du spionerar på en procedur så är du faktiskt inte enhetstestad den proceduren, snarare gör du det lättare för den andra proceduren som är relaterad till proceduren du spionerar att enhetstestas.
Till exempel, i vårt fall, om vi vill enhetstesta huvudproceduren måste vi håna verktygsproceduren genom att använda spionproceduren som kommer att göra det lättare för oss att enhetstesta huvudproceduren.
Skapa enhetstest för huvudprocedur för spionprogram
Skapa ett databasenhetstest för att kontrollera att huvudproceduren fungerar korrekt.
Den här artikeln fungerar för dbForge Studio för SQL Server (eller endast dbForge Unit Test) och SSMS (SQL Server Management Studio) . Observera dock att när du använder SSMS (SQL Server Management Studio) antar jag att du redan har installerat tSQLt Framework och redo att skriva enhetstesten.
För att skapa det första databasenhetstestet, högerklicka på SQLBookShop-databasen. På genvägsmenyn klickar du på Enhetstest och sedan på Lägg till nytt test enligt följande:
Skriv enhetens testkod:
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
Kör enhetstest för huvudprocedur
Kör enhetstestet:
Grattis, du har framgångsrikt enhetstestat en lagrad procedur genom att isolera den från dess verktygsprocedur efter att ha använt spionprocedur.
För mer information om enhetstestning, gå igenom följande delar av min tidigare artikel om testdriven databasutveckling (TDDD):
- Hoppa till Start Testdriven Database Development (TDDD) – Del 1
- Hoppa till Start Testdriven Database Development (TDDD) – Del 2
- Hoppa till Start Testdriven Database Development (TDDD) – Del 3
Saker att göra
Du kan nu skapa databasenhetstester för lite komplexa scenarier där lagrade procedurer anropar verktygsprocedurer.
- Försök att ändra spionproceduren @CommandToExecute argument (värde) som @CommandToExecute =‘set @DayType =”Ingenting” ‘ och se att testet kommer att misslyckas nu
- Försök att uppfylla affärskraven i den här artikeln genom att använda testdriven databasutveckling (TDDD)
- Försök att uppfylla ett annat affärskrav för att se den senaste beställningen som lagts av en kund som använder testdriven utveckling (TDDD) som involverar samma verktygsprocedur
- Försök att skapa ett enhetstest för verktygsprocedur genom att isolera huvudproceduren
- Ge dig själv ett försök att skapa ett enhetstest för en procedur som anropar två verktygsprocedurer
Användbart verktyg:
dbForge Unit Test – ett intuitivt och bekvämt gränssnitt för att implementera automatiserad enhetstestning i SQL Server Management Studio.