Database
 sql >> Datenbank >  >> RDS >> Database

Vereinfachen des Testens von Einheiten Hauptspeicherprozedur, die auch eine Hilfsprozedur aufruft

Dieser Artikel bietet eine exemplarische Vorgehensweise zum Testen von Datenbankkomponenten einer gespeicherten Prozedur, die eine Dienstprogrammprozedur enthält.

In diesem Artikel werde ich ein Szenario zum Testen von Datenbankkomponenten diskutieren, bei dem eine gespeicherte Hauptprozedur von einer Hilfsprozedur abhängt und die Hauptprozedur einem Komponententest unterzogen werden muss, um sicherzustellen, dass die Anforderungen erfüllt werden. Der Schlüssel liegt darin, sicherzustellen, dass ein Unit-Test nur für eine einzelne Codeeinheit geschrieben werden kann, was bedeutet, dass wir einen Unit-Test für die Hauptprozedur und einen weiteren Unit-Test für die Utility-Prozedur benötigen.

Das Unit-Testen einer einzelnen gespeicherten Prozedur ist einfacher als das Unit-Testen einer Prozedur, die eine Utility-Prozedur in ihrem Code aufruft.

Es ist sehr wichtig, das Dienstprogrammprozedur-Szenario zu verstehen und warum es anders ist als das Komponententesten einer normalen gespeicherten Prozedur.

Szenario:Hilfsverfahren innerhalb des Hauptverfahrens

Um das Szenario des Versorgungsverfahrens zu verstehen, beginnen wir mit der Definition und dem Beispiel des Versorgungsverfahrens:

Was ist ein Hilfsverfahren

Eine Utility-Prozedur ist im Allgemeinen eine kleine Prozedur, die von der/den Hauptprozedur(en) verwendet wird, um eine bestimmte Aufgabe zu erledigen, wie z. B. etwas für die Hauptprozedur zu erhalten oder der Hauptprozedur etwas hinzuzufügen.

Eine andere Definition von Hilfsprozedur ist eine kleine gespeicherte Prozedur, die zu Wartungszwecken geschrieben wurde und Systemtabellen oder -ansichten beinhalten kann, die von einer beliebigen Anzahl von Prozeduren oder sogar direkt aufgerufen werden.

Beispiele für Hilfsverfahren

Stellen Sie sich ein Kundenbestellungs-Produkt-Szenario vor, in dem ein Kunde eine Bestellung für ein bestimmtes Produkt aufgibt. Wenn wir die Hauptprozedur erstellen, um uns alle von einem bestimmten Kunden aufgegebenen Bestellungen zu erhalten, kann eine Hilfsprozedur verwendet werden, um uns zu helfen zu verstehen, ob jede Bestellung von dem Kunden an einem Wochentag oder Wochenende aufgegeben wurde.
Auf diese Weise a kleine Utility-Prozedur kann geschrieben werden, um „Weekday“ oder „Weekend“ zurückzugeben, basierend auf dem Datum, an dem das Produkt vom Kunden bestellt wurde.

Ein weiteres Beispiel sind gespeicherte Systemprozeduren wie „sp_server_info“ in der Master-Datenbank, die Informationen zur installierten Version von SQL Server bereitstellen:

EXEC sys.sp_server_info

Warum das Unit-Testing-Utility-Verfahren anders ist

Wie bereits erwähnt, ist das Unit-Testen einer Hilfsprozedur, die innerhalb der Hauptprozedur aufgerufen wird, etwas komplizierter als das Unit-Testen einer einfachen gespeicherten Prozedur.

In Anbetracht des oben erwähnten Beispiels für ein Kundenauftragsprodukt müssen wir einen Einheitentest schreiben, um zu überprüfen, ob die Utility-Prozedur einwandfrei funktioniert, und es muss auch ein Einheitentest geschrieben werden, um zu überprüfen, ob die Hauptprozedur, die die Utility-Prozedur aufruft, auch ordnungsgemäß funktioniert und sich trifft die Geschäftsanforderung(en).

Dies wird wie folgt dargestellt:

Isolation von Utility/Main Procedure Challenge

Die größte Herausforderung beim Schreiben eines oder mehrerer Komponententests für die Prozedur, die eine Utility-Prozedur beinhaltet, besteht darin, sicherzustellen, dass wir uns beim Schreiben eines Komponententests für die Hauptprozedur keine Gedanken über die Funktionsweise der Utility-Prozedur machen sollten, und dasselbe gilt für die Utility-Prozedur . Dies ist eine herausfordernde Aufgabe, die beim Schreiben von Komponententests für ein solches Szenario im Auge behalten werden muss.
Die Isolierung von der Hilfs- oder Hauptprozedur ist ein Muss, je nachdem, welche Prozedur getestet wird. Folgende Dinge müssen wir im Zusammenhang mit dem Isolieren beim Unit-Testen beachten:

  1. Isolieren von der Utility-Prozedur beim Komponententest der Hauptprozedur.
  2. Isolieren von der Hauptprozedur beim Unit-Test-Utility-Prozedur.

Bitte denken Sie daran, dass sich dieser Artikel auf das Komponententesten der Hauptprozedur konzentriert, indem sie von ihrer Hilfsprozedur isoliert wird.

Hauptprozedur und ihre Hilfsprozedur erstellen

Um einen Komponententest für ein Szenario zu schreiben, in dem die Utility-Prozedur von der Hauptprozedur verwendet wird, müssen wir zunächst die folgenden Voraussetzungen erfüllen:

  1. Beispieldatenbank
  2. Geschäftsanforderung(en)

Beispieldatenbank einrichten (SQLBookShop)

Wir erstellen eine einfache Beispieldatenbank mit zwei Tabellen namens „SQLBookShop“, die die Datensätze aller bestellten Bücher enthält, wie unten gezeigt:

Erstellen Sie die SQLBookShop-Beispieldatenbank wie folgt:

-- (1) Create SQLBookShop database
  CREATE DATABASE SQLBookShop;
  GO

Erstellen und füllen Sie Datenbankobjekte (Tabellen) wie folgt:

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

Quick Check – Musterdatenbank

Führen Sie eine schnelle Datenbankprüfung durch, indem Sie die OrderedBooks-Ansicht mit dem folgenden Code ausführen:

USE SQLBookShop

-- Run OrderedBooks view
SELECT
  ob.OrderID
 ,ob.OrderDate
 ,ob.Title AS BookTitle
 ,ob.Quantity
 ,ob.TotalPrice
FROM dbo.OrderedBooks ob

Bitte beachten Sie, dass ich dbForge Studio für SQL Server verwende, sodass die Ausgabe möglicherweise anders aussieht, wenn Sie denselben Code in SSMS (SQL Server Management Studio) ausführen. Es gibt jedoch keinen Unterschied zwischen Skripten und ihren Ergebnissen.

Geschäftsanforderung, um die neueste Bestellung mit zusätzlichen Informationen anzuzeigen

Eine geschäftliche Anforderung wurde an das Entwicklerteam gesendet, die besagt:„Der Endbenutzer möchte wissen, welche Bestellung für ein bestimmtes Buch zuletzt aufgegeben wurde, zusammen mit der Information, ob die Bestellung an einem Wochentag oder Wochenende aufgegeben wurde.“

Ein Wort zu TDDD

Wir folgen in diesem Artikel nicht strikt der testgetriebenen Datenbankentwicklung (TDDD), aber ich empfehle dringend, die testgetriebene Datenbankentwicklung (TDDD) zu verwenden, um sowohl Haupt- als auch Hilfsprozeduren zu erstellen, die mit der Erstellung eines Einheitentests beginnt, um zu prüfen, ob ein Objekt vorhanden ist schlägt zunächst fehl, gefolgt von der Erstellung des Objekts und der erneuten Ausführung des Komponententests, der bestanden werden muss.
Eine detaillierte Anleitung finden Sie im ersten Teil dieses Artikels.

Dienstprogrammverfahren identifizieren

Angesichts der geschäftlichen Anforderungen ist eines sicher:Wir brauchen eine Utility-Prozedur, die uns sagen kann, ob ein bestimmtes Datum ein Wochentag oder ein Wochenende ist.

Dienstprogrammprozedur erstellen (GetDayType)

Erstellen Sie eine Utility-Prozedur und nennen Sie sie „GetDayType“ wie folgt:

-- 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

Quick Check – Utility-Verfahren

Schreiben Sie die folgenden Codezeilen, um die Dienstprogrammprozedur schnell zu überprüfen:

-- Quick check utility procedure
declare @DayType varchar(10)
EXEC uspGetDayType '20181001',@DayType output
select @DayType AS [Type of Day]

Hauptprozedur erstellen (GetLatestOrderByBookId)

Erstellen Sie die Hauptprozedur, um die letzte Bestellung für ein bestimmtes Buch anzuzeigen und auch, ob die Bestellung an einem Wochentag oder Wochenende aufgegeben wurde, und nennen Sie sie „GetLatestOrderByBookId“, die den Aufruf für die Utility-Prozedur wie folgt enthält:

-- 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

Quick Check – Hauptverfahren

Führen Sie den folgenden Code aus, um zu sehen, ob die Prozedur gut funktioniert oder nicht:

-- Get latest order for the bookid=6
EXEC uspGetLatestOrderByBookId @BookId = 6

Einheitentest-Hauptprozedur, die Hilfsprozedur aufruft

Der Schlüssel hier ist, den Unterschied zwischen dem Unit-Testing-Hauptverfahren und dem Utility-Verfahren zu verstehen.

Wir konzentrieren uns derzeit auf das Komponententesten der Hauptprozedur, was bedeutet, dass die Utility-Prozedur ordnungsgemäß von diesem Komponententest isoliert werden muss.

Verwendung von Spionageverfahren

Um sicherzustellen, dass der Unit-Test der Hauptprozedur sich weiterhin auf das Testen der Funktionalität der Hauptprozedur konzentriert, müssen wir die von tSQLt bereitgestellte Spionageprozedur verwenden, die als Stub (Platzhalter) für die Hilfsprozedur fungieren wird.

Laut tsqlt.org denken Sie bitte daran, dass Sie, wenn Sie eine Prozedur ausspionieren, diese Prozedur nicht wirklich testen, sondern es einfacher machen, dass die andere Prozedur, die sich auf die Prozedur bezieht, die Sie ausspionieren, getestet wird.

Wenn wir zum Beispiel in unserem Fall die Hauptprozedur komponententesten wollen, müssen wir die Dienstprogrammprozedur durch die Verwendung der Spionageprozedur verspotten, was uns den Komponententest der Hauptprozedur erleichtern wird.

Erstellen eines Einheitentests für das Hauptprozedur-Spionagedienstprogramm

Erstellen Sie einen Datenbank-Einheitentest, um die Hauptprozedurfunktionen ordnungsgemäß zu überprüfen.

Dieser Artikel gilt für dbForge Studio für SQL Server (oder nur dbForge Unit Test) und SSMS (SQL Server Management Studio) . Beachten Sie jedoch, dass Sie bei Verwendung von SSMS (SQL Server Management Studio) davon ausgehen, dass Sie das tSQLt-Framework bereits installiert haben und bereit sind, die Komponententests zu schreiben.

Um den ersten Datenbankkomponententest zu erstellen, klicken Sie mit der rechten Maustaste auf die SQLBookShop-Datenbank. Klicken Sie im Kontextmenü auf Unit Test und dann wie folgt auf Neuen Test hinzufügen:

Schreiben Sie den Einheitentestcode:

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

Einheitentest für Hauptverfahren ausführen

Führen Sie den Komponententest aus:

Herzlichen Glückwunsch, Sie haben eine gespeicherte Prozedur erfolgreich getestet, indem Sie sie nach der Verwendung der Spionageprozedur von ihrer Dienstprogrammprozedur isoliert haben.

Weitere Informationen zu Unit-Tests finden Sie in den folgenden Abschnitten meines vorherigen Artikels über testgetriebene Datenbankentwicklung (TDDD):

  • Jump to Start Test-Driven Database Development (TDDD) – Teil 1
  • Jump to Start Test-Driven Database Development (TDDD) – Teil 2
  • Jump to Start Test-Driven Database Development (TDDD) – Teil 3

Dinge zu tun

Sie können jetzt Datenbankkomponententests für etwas komplexe Szenarien erstellen, in denen gespeicherte Prozeduren Dienstprogrammprozeduren aufrufen.

  1. Bitte versuchen Sie, das @CommandToExecute-Argument (Wert) der Spionageprozedur in @CommandToExecute =‘set @DayType =”Nothing”’ zu ändern, und sehen Sie, dass der Test jetzt fehlschlagen wird
  2. Bitte versuchen Sie, die Geschäftsanforderungen in diesem Artikel zu erfüllen, indem Sie testgetriebene Datenbankentwicklung (TDDD) verwenden
  3. Bitte versuchen Sie, eine andere Geschäftsanforderung zu erfüllen, um die letzte Bestellung anzuzeigen, die von einem Kunden mit testgetriebener Entwicklung (TDDD) mit demselben Dienstprogrammverfahren aufgegeben wurde
  4. Bitte versuchen Sie, einen Einheitentest für die Utility-Prozedur zu erstellen, indem Sie die Hauptprozedur isolieren
  5. Bitte versuchen Sie selbst, einen Komponententest für eine Prozedur zu erstellen, die zwei Hilfsprozeduren aufruft

Nützliches Tool:

dbForge Unit Test – eine intuitive und komfortable GUI zur Implementierung automatisierter Unit-Tests in SQL Server Management Studio.