Ten artykuł zawiera opis testowania jednostki bazy danych procedury składowanej, która zawiera w sobie procedurę narzędziową.
W tym artykule omówię scenariusz testowania jednostkowego bazy danych, w którym główna procedura składowana zależy od procedury narzędziowej, a główna procedura musi zostać przetestowana jednostkowo, aby upewnić się, że wymagania są spełnione. Kluczem jest upewnienie się, że test jednostkowy może być napisany tylko dla pojedynczej jednostki kodu, co oznacza, że potrzebujemy jednego testu jednostkowego dla procedury głównej i innego testu jednostkowego dla procedury użytkowej.
Testowanie jednostkowe pojedynczej procedury składowanej jest łatwiejsze w porównaniu z testowaniem jednostkowym procedury, która wywołuje procedurę narzędziową w swoim kodzie.
Bardzo ważne jest zrozumienie scenariusza procedury narzędziowej i tego, dlaczego różni się ona od testowania jednostkowego normalnej procedury składowanej.
Scenariusz:Procedura narzędziowa w ramach procedury głównej
W celu zrozumienia scenariusza procedury użytkowej zacznijmy od definicji i przykładu procedury użytkowej:
Co to jest procedura narzędziowa
Procedura narzędziowa to na ogół niewielka procedura używana przez główne procedury do wykonania określonego zadania, takiego jak uzyskanie czegoś dla procedury głównej lub dodanie czegoś do procedury głównej.
Inną definicją procedury narzędziowej jest mała procedura składowana napisana do celów konserwacyjnych, która może obejmować tabele systemowe lub widoki, które mogą być wywoływane przez dowolną liczbę procedur lub nawet bezpośrednio.
Przykłady procedury użytkowej
Pomyśl o scenariuszu klient-zamówienie-produkt, w którym klient składa zamówienie na konkretny produkt. Jeśli utworzymy główną procedurę, aby uzyskać wszystkie zamówienia złożone przez konkretnego klienta, wówczas procedura użyteczności może nam pomóc w zrozumieniu, czy każde zamówienie zostało złożone przez klienta w dzień powszedni czy w weekend.
W ten sposób można napisać procedurę małej użyteczności, aby zwrócić „dzień tygodnia” lub „weekend” na podstawie daty zamówienia produktu przez klienta.
Innym przykładem mogą być procedury składowane w systemie, takie jak „sp_server_info” w głównej bazie danych, które dostarczają informacji o wersji zainstalowanej SQL Server:
EXEC sys.sp_server_info
Dlaczego procedura testowania jednostek jest inna
Jak wspomniano wcześniej, testowanie jednostkowe procedury użytkowej, która jest wywoływana wewnątrz głównej procedury, jest nieco skomplikowane niż testowanie jednostkowe prostej procedury składowanej.
Biorąc pod uwagę wspomniany powyżej przykład zamówienia klienta, musimy napisać test jednostkowy, aby sprawdzić, czy procedura użyteczności działa poprawnie, a także należy napisać test jednostkowy, aby sprawdzić, czy główna procedura, która wywołuje procedurę użyteczności, również działa poprawnie, a także spełnia wymagania biznesowe.
Jest to zilustrowane następująco:
Izolowanie od wyzwania użyteczności/procedury głównej
Głównym wyzwaniem przy pisaniu testów jednostkowych dla procedury, która zawiera procedurę narzędziową, jest upewnienie się, że nie powinniśmy martwić się o działanie procedury użytkowej podczas pisania testu jednostkowego dla procedury głównej i to samo dotyczy procedury narzędziowej . Jest to trudne zadanie, o którym należy pamiętać podczas pisania testów jednostkowych dla takiego scenariusza.
Odizolowanie się od narzędzia lub procedury głównej jest koniecznością, w zależności od tego, która procedura jest testowana. Musimy pamiętać o następujących rzeczach w kontekście izolowania podczas testów jednostkowych:
- Izolowanie od procedury narzędziowej podczas testowania jednostki głównej procedury.
- Izolowanie od głównej procedury podczas procedury narzędziowej testowania jednostek.
Pamiętaj, że ten artykuł koncentruje się na testowaniu jednostki głównej procedury poprzez oddzielenie jej od procedury użytkowej.
Tworzenie głównej procedury i jej procedury użytkowej
Aby napisać test jednostkowy dla scenariusza, w którym procedura narzędzia jest używana przez procedurę główną, najpierw musimy spełnić następujące wymagania wstępne:
- Przykładowa baza danych
- Wymagania biznesowe
Konfiguracja przykładowej bazy danych (SQLBookShop)
Tworzymy prostą dwutabelową przykładową bazę danych o nazwie „SQLBookShop”, która zawiera rekordy wszystkich uporządkowanych książek, jak pokazano poniżej:
Utwórz przykładową bazę danych SQLBookShop w następujący sposób:
-- (1) Create SQLBookShop database CREATE DATABASE SQLBookShop; GO
Utwórz i wypełnij obiekty bazy danych (tabele) w następujący sposób:
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
Szybkie sprawdzenie – przykładowa baza danych
Wykonaj szybkie sprawdzenie bazy danych, uruchamiając widok Książki zamówione za pomocą następującego kodu:
USE SQLBookShop -- Run OrderedBooks view SELECT ob.OrderID ,ob.OrderDate ,ob.Title AS BookTitle ,ob.Quantity ,ob.TotalPrice FROM dbo.OrderedBooks ob
Pamiętaj, że używam dbForge Studio dla SQL Server, więc wygląd danych wyjściowych może się różnić, jeśli uruchomisz ten sam kod w SSMS (SQL Server Management Studio). Jednak nie ma różnicy między skryptami a ich wynikami.
Wymaganie biznesowe, aby zobaczyć najnowsze zamówienie z dodatkowymi informacjami
Do zespołu programistów zostało wysłane wymaganie biznesowe, które stwierdza, że „użytkownik końcowy chce wiedzieć o ostatnim zamówieniu złożonym na konkretną książkę wraz z informacją, czy zamówienie zostało złożone w dzień powszedni, czy w weekend”
Słowo o TDDD
W tym artykule nie podążamy ściśle za tworzeniem baz danych sterowanych testami (TDDD), ale zdecydowanie zalecam korzystanie z rozwoju baz danych sterowanego testami (TDDD) do tworzenia procedur głównych i narzędziowych, które rozpoczynają się od stworzenia testu jednostkowego, aby sprawdzić, czy istnieje obiekt, który na początku kończy się niepowodzeniem, po czym następuje utworzenie obiektu i ponowne uruchomienie testu jednostkowego, który musi przejść.
Aby uzyskać szczegółowy opis, zapoznaj się z pierwszą częścią tego artykułu.
Procedura identyfikacji narzędzia
Widząc wymagania biznesowe, jedno jest pewne, że potrzebujemy procedury narzędziowej, która może nam powiedzieć, czy dana data jest dniem tygodnia, czy weekendem.
Procedura tworzenia narzędzia (GetDayType)
Utwórz procedurę narzędzia i nazwij ją „GetDayType” w następujący sposób:
-- 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
Szybkie sprawdzenie – procedura narzędziowa
Napisz następujące wiersze kodu, aby szybko sprawdzić procedurę narzędzia:
-- Quick check utility procedure declare @DayType varchar(10) EXEC uspGetDayType '20181001',@DayType output select @DayType AS [Type of Day]
Tworzenie głównej procedury (GetLatestOrderByBookId)
Utwórz główną procedurę, aby zobaczyć ostatnie zamówienie złożone dla konkretnej książki, a także czy zamówienie zostało złożone w dzień powszedni czy w weekend i nazwij ją „GetLatestOrderByBookId”, która zawiera wywołanie procedury narzędzia w następujący sposób:
-- 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
Szybkie sprawdzenie – procedura główna
Uruchom następujący kod, aby sprawdzić, czy procedura działa poprawnie, czy nie:
-- Get latest order for the bookid=6 EXEC uspGetLatestOrderByBookId @BookId = 6
Procedura główna testowania jednostki Procedura wywoływania narzędzia
Kluczem jest tutaj zrozumienie różnicy między testowaniem jednostkowym głównej procedury a procedurą użytkową.
Obecnie koncentrujemy się na testowaniu jednostkowym głównej procedury, co oznacza, że procedura narzędziowa musi być bezpiecznie odizolowana od tego testu jednostkowego.
Korzystanie z procedury szpiegowskiej
Aby upewnić się, że test jednostki głównej procedury pozostaje skupiony na testowaniu funkcjonalności procedury głównej, musimy użyć procedury szpiegowskiej dostarczonej przez tSQLt, która będzie działać jako skrót (zastępczy) dla procedury użytkowej.
Według tsqlt.org pamiętaj, że jeśli szpiegujesz procedurę, w rzeczywistości nie testujesz tej procedury jednostkowo, a raczej ułatwiasz testowanie jednostkowe innej procedury związanej z procedurą, którą szpiegujesz.
Na przykład, w naszym przypadku, jeśli chcemy przetestować główną procedurę jednostkową, musimy zakpić procedurę użytkową za pomocą procedury szpiegowskiej, która ułatwi nam testowanie jednostkowe głównej procedury.
Tworzenie testu jednostkowego dla procedury głównej Procedura narzędzia szpiegującego
Utwórz test jednostkowy bazy danych, aby sprawdzić, czy główna procedura działa poprawnie.
Ten artykuł działa dla dbForge Studio dla SQL Server (lub tylko dbForge Unit Test) i SSMS (SQL Server Management Studio) . Należy jednak pamiętać, że podczas korzystania z SSMS (SQL Server Management Studio) zakładam, że już zainstalowałeś tSQLt Framework i jesteś gotowy do pisania testów jednostkowych.
Aby utworzyć pierwszy test jednostkowy bazy danych, kliknij prawym przyciskiem myszy bazę danych SQLBookShop. W menu skrótów kliknij Test jednostkowy, a następnie Dodaj nowy test w następujący sposób:
Wpisz kod testu jednostkowego:
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
Wykonywanie testu jednostkowego dla procedury głównej
Uruchom test jednostkowy:
Gratulacje, pomyślnie przetestowałeś procedurę składowaną, izolując ją od jej procedury użytkowej po użyciu procedury szpiegowskiej.
Aby uzyskać więcej informacji na temat testów jednostkowych, zapoznaj się z następującymi częściami mojego poprzedniego artykułu o tworzeniu baz danych sterowanych testami (TDDD):
- Skok, aby rozpocząć tworzenie baz danych oparte na testach (TDDD) – część 1
- Skok, aby rozpocząć tworzenie baz danych oparte na testach (TDDD) – część 2
- Skok, aby rozpocząć tworzenie baz danych oparte na testach (TDDD) – część 3
Rzeczy do zrobienia
Możesz teraz tworzyć testy jednostkowe bazy danych dla nieco złożonych scenariuszy, w których procedury składowane wywołują procedury narzędziowe.
- Spróbuj zmienić procedurę szpiegowską @CommandToExecute argument (wartość) jako @CommandToExecute =‘set @DayType =”Nic” ‘i zobacz, że test teraz się nie powiedzie
- Spróbuj spełnić wymagania biznesowe opisane w tym artykule, korzystając z programowania baz danych opartego na testach (TDDD)
- Spróbuj spełnić inne wymagania biznesowe, aby zobaczyć najnowsze zamówienie złożone przez dowolnego klienta za pomocą programowania sterowanego testami (TDDD) obejmującego tę samą procedurę narzędziową
- Spróbuj utworzyć test jednostkowy dla procedury użytkowej, izolując procedurę główną
- Proszę, spróbuj stworzyć test jednostkowy dla procedury, która wywołuje dwie procedury narzędziowe
Przydatne narzędzie:
dbForge Unit Test – intuicyjny i wygodny GUI do wdrażania zautomatyzowanych testów jednostkowych w SQL Server Management Studio.