Tento článek poskytuje návod na testování databázové jednotky uložené procedury, která obsahuje proceduru obslužného programu.
V tomto článku budu diskutovat o scénáři testování databázové jednotky, kdy hlavní uložená procedura závisí na proceduře obslužného programu a hlavní procedura musí být testována na jednotku, aby se zajistilo, že jsou splněny požadavky. Klíčem je zajistit, aby jednotkový test mohl být zapsán pouze pro jednu jednotku kódu, což znamená, že potřebujeme jeden unit test pro hlavní proceduru a další unit test pro obslužnou proceduru.
Unit testování jedné uložené procedury je snazší ve srovnání s unit testováním procedury, která ve svém kódu volá obslužnou proceduru.
Je velmi důležité porozumět scénáři obslužné procedury a proč se liší od testování jednotky běžné uložené procedury.
Scénář:Obslužný postup v rámci hlavního postupu
Abychom porozuměli scénáři procedury utility, začněme definicí a příkladem procedury utility:
Co je to obslužný postup
Obslužná procedura je obecně malá procedura, kterou hlavní procedura(y) používá k provedení nějakého konkrétního úkolu, jako je získání něčeho pro hlavní proceduru nebo přidání něčeho do hlavní procedury.
Další definicí obslužné procedury je malá uložená procedura napsaná pro účely údržby, která může zahrnovat systémové tabulky nebo pohledy, které lze volat libovolným počtem procedur nebo dokonce přímo.
Příklady obslužného postupu
Představte si scénář zákazník-objednávka-produkt, kde zákazník zadá objednávku na konkrétní produkt. Pokud vytvoříme hlavní postup, abychom dostali všechny objednávky zadané konkrétním zákazníkem, pak lze použít obslužný postup, který nám pomůže pochopit, zda byla každá objednávka zadána zákazníkem ve všední den nebo o víkendu.
Tímto způsobem lze Procedura malého obslužného programu může být napsána tak, že vrátí „Den v týdnu“ nebo „Víkend“ na základě data, kdy si zákazník objednal produkt.
Dalším příkladem mohou být systémové uložené procedury, jako je „sp_server_info“ v hlavní databázi, která poskytuje informace o nainstalované verzi SQL Serveru:
EXEC sys.sp_server_info
Proč je postup nástroje Unit Testing odlišný
Jak již bylo zmíněno dříve, jednotkové testování obslužné procedury, která je volána uvnitř hlavní procedury, je mírně komplikované než jednotkové testování jednoduché uložené procedury.
Vzhledem k výše uvedenému příkladu zákaznického produktu musíme napsat test jednotky, abychom ověřili, že procedura obslužného programu funguje správně, a také je třeba napsat test jednotky, abychom ověřili, že hlavní procedura, která volá proceduru utility, také funguje správně, stejně jako schůzka obchodní požadavky.
To je znázorněno následovně:
Izolace od výzvy utility/hlavní procedury
Hlavním problémem při psaní testů jednotek pro proceduru, která zahrnuje proceduru utility, je ujistit se, že bychom se neměli starat o fungování procedury utility při psaní testu jednotky pro hlavní proceduru a totéž platí pro proceduru utility. . Toto je náročný úkol, který je třeba mít na paměti při psaní jednotkových testů pro takový scénář.
Oddělení od obslužného programu nebo hlavní procedury je nutností v závislosti na tom, která procedura je testována. V souvislosti s izolací při testování jednotky musíme mít na paměti následující věci:
- Oddělení od obslužné procedury při testování jednotky hlavní procedury.
- Oddělení od hlavní procedury při testování jednotky procedury utility.
Nezapomeňte, že tento článek je zaměřen na testování jednotky hlavního postupu tím, že jej izoluje od jeho obslužného postupu.
Vytvoření hlavní procedury a její pomocné procedury
Abychom mohli napsat test jednotky pro scénář, kde hlavní procedura používá obslužnou proceduru, musíme mít nejprve následující předpoklady:
- Ukázková databáze
- Obchodní požadavky
Nastavení ukázkové databáze (SQLBookShop)
Vytváříme jednoduchou dvoutabulkovou vzorovou databázi s názvem „SQLBookShop“, která obsahuje záznamy všech knih objednaných, jak je uvedeno níže:
Vytvořte ukázkovou databázi SQLBookShop následovně:
-- (1) Create SQLBookShop database CREATE DATABASE SQLBookShop; GO
Vytvořte a naplňte databázové objekty (tabulky) následovně:
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
Rychlá kontrola – vzorová databáze
Proveďte rychlou kontrolu databáze spuštěním zobrazení OrderedBooks pomocí následujícího kódu:
USE SQLBookShop -- Run OrderedBooks view SELECT ob.OrderID ,ob.OrderDate ,ob.Title AS BookTitle ,ob.Quantity ,ob.TotalPrice FROM dbo.OrderedBooks ob
Vezměte prosím na vědomí, že používám dbForge Studio pro SQL Server, takže vzhled výstupu se může lišit, pokud stejný kód spustíte v SSMS (SQL Server Management Studio). Mezi skripty a jejich výsledky však není žádný rozdíl.
Obchodní požadavek na zobrazení nejnovější objednávky s dalšími informacemi
Vývojovému týmu byl zaslán obchodní požadavek, který uvádí, že „Koncový uživatel chce vědět o poslední objednávce konkrétní knihy spolu s informací, zda byla objednávka zadána ve všední den nebo o víkendu.“
Něco o TDDD
V tomto článku se striktně neřídíme testem řízeným vývojem databází (TDDD), ale důrazně doporučuji použít vývoj databází řízený testem (TDDD) k vytvoření jak hlavních, tak pomocných procedur, které začínají vytvořením testu jednotky pro kontrolu, zda existuje objekt, který nejprve selže, následuje vytvoření objektu a opětovné spuštění testu jednotky, který musí projít.
Podrobný návod naleznete v první části tohoto článku.
Identifikace postupu nástroje
Když vidíme obchodní požadavek, jedna věc je jistá, že potřebujeme obslužný postup, který nám řekne, zda je konkrétní datum všední den nebo víkend.
Vytvoření procedury nástroje (GetDayType)
Vytvořte obslužnou proceduru a nazvěte ji „GetDayType“ následovně:
-- 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
Rychlá kontrola – postup nástroje
Napište následující řádky kódu pro rychlou kontrolu postupu nástroje:
-- Quick check utility procedure declare @DayType varchar(10) EXEC uspGetDayType '20181001',@DayType output select @DayType AS [Type of Day]
Vytvoření hlavní procedury (GetLatestOrderByBookId)
Vytvořte hlavní proceduru, abyste viděli poslední objednávku zadanou pro konkrétní knihu a také to, zda byla objednávka zadána ve všední den nebo víkend, a nazvěte ji „GetLatestOrderByBookId“, která obsahuje volání procedury nástroje takto:
-- 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
Rychlá kontrola – hlavní postup
Spusťte následující kód a zjistěte, zda postup funguje dobře nebo ne:
-- Get latest order for the bookid=6 EXEC uspGetLatestOrderByBookId @BookId = 6
Testování hlavní procedury volání procedury utility
Klíčem je zde pochopit rozdíl mezi jednotkovým testováním hlavního postupu a obslužného postupu.
V současné době se zaměřujeme na testování hlavní procedury, takže to znamená, že procedura obslužného programu musí být elegantně izolována od tohoto testu jednotky.
Použití špionážního postupu
Abychom se ujistili, že test hlavní procedury zůstane zaměřen na testování funkčnosti hlavní procedury, musíme použít špionážní proceduru poskytovanou tSQLt, která bude fungovat jako stub (placeholder) pro proceduru utility.
Podle tsqlt.org mějte prosím na paměti, že pokud špehujete proceduru, ve skutečnosti tuto proceduru netestujete, ale usnadňujete testování jednotky.
Například v našem případě, pokud chceme testovat hlavní proceduru jednotky, musíme zesměšňovat proceduru nástroje pomocí procedury spy, která nám usnadní testování hlavní procedury.
Vytvoření testu jednotky pro hlavní proceduru špionážní procedura
Vytvořte test databázové jednotky pro správnou kontrolu funkcí hlavní procedury.
Tento článek funguje pro dbForge Studio pro SQL Server (nebo pouze dbForge Unit Test) a SSMS (SQL Server Management Studio) . Upozorňujeme však, že při používání SSMS (SQL Server Management Studio) předpokládám, že jste již nainstalovali tSQLt Framework a jste připraveni psát testy jednotek.
Chcete-li vytvořit první test databázové jednotky, klikněte pravým tlačítkem na databázi SQLBookShop. V místní nabídce klikněte na Test jednotky a poté na Přidat nový test následovně:
Napište kód testu jednotky:
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
Spuštění testu jednotky pro hlavní postup
Spusťte test jednotky:
Gratulujeme, úspěšně jste jednotku otestovali uloženou proceduru tak, že jste ji po použití spy procedury izolovali od její obslužné procedury.
Další informace o testování jednotek naleznete v následujících částech mého předchozího článku o vývoji databází řízených testy (TDDD):
- Jump to Start Test-Driven Database Development (TDDD) – Part 1
- Jump to Start Test-Driven Database Development (TDDD) – Part 2
- Jump to Start Test-Driven Database Development (TDDD) – Part 3
Co dělat
Nyní můžete vytvářet testy databázových jednotek pro mírně složité scénáře, kde uložené procedury volají pomocné procedury.
- Zkuste prosím změnit špionážní proceduru @CommandToExecute argument (hodnotu) na @CommandToExecute ='set @DayType =”Nothing” “ a uvidíte, že test nyní selže
- Zkuste prosím splnit obchodní požadavky v tomto článku pomocí testování databázového vývoje (TDDD)
- Zkuste prosím splnit další obchodní požadavek, abyste viděli nejnovější objednávku zadanou kterýmkoli zákazníkem využívajícím vývoj řízený testem (TDDD) se stejným obslužným postupem
- Zkuste vytvořit test jednotky pro obslužný postup izolováním hlavního postupu
- Zkuste si vytvořit jednotkový test pro proceduru, která volá dvě pomocné procedury
Užitečný nástroj:
dbForge Unit Test – intuitivní a pohodlné GUI pro implementaci automatizovaného testování jednotek v SQL Server Management Studio.