sql >> Database teknologi >  >> RDS >> Database

Forenkling af enhedstest Hovedlagret procedure, som også kalder en hjælpeprocedure

Denne artikel giver en gennemgang af databaseenhed, der tester en lagret procedure, som indeholder en hjælpeprocedure i den.

I denne artikel vil jeg diskutere et databaseenhedstestscenario, når en lagret hovedprocedure afhænger af en hjælpeprocedure, og hovedproceduren skal enhedstestes for at sikre, at kravene er opfyldt. Nøglen er at sikre, at en enhedstest kun kan skrives for en enkelt kodeenhed, hvilket betyder, at vi har brug for én enhedstest til hovedproceduren og en anden enhedstest til brugsproceduren.

Enhedstest af en enkelt lagret procedure er lettere sammenlignet med enhedstest af en procedure, der kalder en hjælpeprocedure inde i sin kode.

Det er meget vigtigt at forstå scenariet for hjælpeprocedurer, og hvorfor det er anderledes end enhedstest af en normal lagret procedure.

Scenarie:Hjælpeprocedure inden for hovedprocedure

For at forstå værktøjsprocedurescenariet lad os begynde med definitionen og eksemplet på værktøjsproceduren:

Hvad er hjælpeprocedure

En hjælpeprocedure er generelt en lille procedure, som bruges af hovedproceduren(erne) til at udføre en bestemt opgave, såsom at få noget til hovedproceduren eller tilføje noget til hovedproceduren.

En anden definition af hjælpeprocedure er en lille lagret procedure skrevet til vedligeholdelsesformål, som kan involvere systemtabeller eller visninger, der skal kaldes af et vilkårligt antal procedurer eller endda direkte.

Eksempler på hjælpeprocedurer

Tænk på et kunde-ordre-produkt-scenarie, hvor en kunde afgiver en ordre på et bestemt produkt. Hvis vi opretter hovedproceduren for at få os alle ordrer afgivet af en bestemt kunde, kan en hjælpeprocedure bruges til at hjælpe os med at forstå, om hver ordre blev afgivet af kunden på hverdage eller weekender.
På denne måde kan en lille hjælpeprocedure kan skrives for at returnere "Weekday" eller "Weekend" baseret på den dato, produktet blev bestilt af kunden.

Et andet eksempel kan være systemlagrede procedurer såsom "sp_server_info" i masterdatabasen, som giver SQL Server installeret versionsinformation:

EXEC sys.sp_server_info

Hvorfor enhedstestværktøjsproceduren er anderledes

Som diskuteret tidligere er enhedstestning af en hjælpeprocedure, som kaldes inde i hovedproceduren, lidt kompliceret end enhedstestning af en simpel lagret procedure.

I betragtning af kunde-ordre-produkt-eksemplet nævnt ovenfor, er vi nødt til at skrive en enhedstest for at kontrollere, at værktøjsproceduren fungerer fint, og der skal også skrives en enhedstest for at kontrollere, at hovedproceduren, som kalder værktøjsproceduren også fungerer korrekt, samt opfylder forretningskravene.

Dette er illustreret som følger:

Isolering fra Utility/Main Procedure Challenge

Hovedudfordringen ved at skrive en enhedstest(er) for proceduren, der involverer en brugsprocedure, er at sikre, at vi ikke skal bekymre os om, hvordan brugsproceduren fungerer, når vi skriver en enhedstest for hovedproceduren, og det samme gælder for brugsproceduren. . Dette er en udfordrende opgave, som man skal huske på, mens man skriver enhedstests til et sådant scenarie.
Isolering fra værktøjet eller hovedproceduren er et must, afhængigt af hvilken procedure der testes. Vi skal huske på følgende ting i forbindelse med isolering under enhedstestning:

  1. Isolering fra hjælpeproceduren ved enhedstest af hovedproceduren.
  2. Isolering fra hovedproceduren ved enhedstest af hjælpeprogram.

Husk venligst, at denne artikel fokuserer på enhedstest af hovedproceduren ved at isolere den fra dens hjælpeprocedure.

Oprettelse af hovedprocedure og dens værktøjsprocedure

For at kunne skrive en enhedstest for et scenarie, hvor hjælpeproceduren er i brug af hovedproceduren, skal vi først have følgende forudsætninger:

  1. Eksempeldatabase
  2. Forretningskrav

Opsætning af prøvedatabase (SQLBookShop)

Vi er ved at oprette en simpel prøvedatabase med to tabeller kaldet "SQLBookShop", som indeholder registreringerne af alle de bøger, der er bestilt som vist nedenfor:

Opret SQLBookShop-eksempeldatabase som følger:

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

Opret og udfyld databaseobjekter (tabeller) som følger:

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

Hurtigt tjek – Eksempeldatabase

Foretag et hurtigt databasetjek ved at køre OrderedBooks-visningen ved hjælp af følgende kode:

USE SQLBookShop

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

Bemærk venligst, at jeg bruger dbForge Studio til SQL Server, så output-looket kan variere, hvis du kører den samme kode i SSMS (SQL Server Management Studio). Der er dog ingen forskel mellem scripts og deres resultater.

Forretningskrav for at se den seneste ordre med yderligere oplysninger

Et forretningskrav er blevet sendt til udviklerteamet, som siger, at "Slutbrugeren ønsker at vide om den seneste ordre afgivet for en bestemt bog sammen med oplysningerne om, hvorvidt ordren blev afgivet på en hverdag eller weekend"

Et ord om TDDD

Vi følger ikke strengt testdrevet databaseudvikling (TDDD) i denne artikel, men jeg anbefaler på det kraftigste at bruge testdrevet databaseudvikling (TDDD) til at skabe både hoved- og hjælpeprocedurer, som begynder med at oprette en enhedstest for at kontrollere, om der findes et objekt, som fejler først, efterfulgt af oprettelse af objektet og genkørsel af enhedstesten, som skal bestå.
For en detaljeret gennemgang, se venligst den første del af denne artikel.

Procedure for identificering af hjælpeprogrammer

Når vi ser forretningskravet, er én ting sikker, vi har brug for en hjælpeprocedure, der kan fortælle os, om en bestemt dato er en hverdag eller en weekend.

Oprettelse af værktøjsprocedure (GetDayType)

Opret en hjælpeprocedure og kald den "GetDayType" som følger:

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

Hurtig tjek – hjælpeprocedure

Skriv følgende linjer med kode for hurtigt at tjekke hjælpeproceduren:

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

Oprettelse af hovedprocedure (GetLatestOrderByBookId)

Opret hovedproceduren for at se den seneste ordre afgivet for en bestemt bog, og også om ordren blev afgivet på en hverdag eller weekend, og kald den "GetLatestOrderByBookId", som indeholder opfordringen til værktøjsproceduren som følger:

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

Hurtig tjek – Hovedprocedure

Kør følgende kode for at se, om proceduren fungerer fint eller ej:

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

Hovedprocedure for enhedstestning af opkaldsværktøj

Nøglen her er at forstå forskellen mellem enhedstest af hovedproceduren og hjælpeproceduren.

Vi er i øjeblikket fokuseret på enhedstest af hovedproceduren, så det betyder, at værktøjsproceduren skal isoleres elegant fra denne enhedstest.

Brug af spionprocedure

For at sikre, at hovedprocedureenhedstesten forbliver fokuseret på at teste funktionaliteten af ​​hovedproceduren, er vi nødt til at bruge spionproceduren leveret af tSQLt, som vil fungere som en stub (pladsholder) for værktøjsproceduren.

Ifølge tsqlt.org skal du huske, at hvis du udspionerer en procedure, er du faktisk ikke enhedsteste den procedure, men du gør det lettere for den anden procedure, der er relateret til den procedure, du spionerer, at blive enhedstestet.

For eksempel, i vores tilfælde, hvis vi ønsker at enhedsteste hovedproceduren, så er vi nødt til at håne hjælpeproceduren ved at bruge spionproceduren, som vil gøre det nemmere for os at enhedsteste hovedproceduren.

Oprettelse af enhedstest for hovedprocedure for spionværktøj

Opret en databaseenhedstest for at kontrollere, at hovedproceduren fungerer korrekt.

Denne artikel fungerer for dbForge Studio til SQL Server (eller kun dbForge Unit Test) og SSMS (SQL Server Management Studio) . Bemærk dog, at når du bruger SSMS (SQL Server Management Studio), antager jeg, at du allerede har installeret tSQLt Framework og klar til at skrive enhedstestene.

For at oprette den første databaseenhedstest skal du højreklikke på SQLBookShop-databasen. På genvejsmenuen skal du klikke på Unit Test og derefter Tilføj ny test som følger:

Skriv enhedstestkoden:

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ørsel af enhedstest for hovedprocedure

Kør enhedstesten:

Tillykke, du har med succes enhedstestet en lagret procedure ved at isolere den fra dens hjælpeprocedure efter brug af spionprocedure.

For mere information om enhedstestning bedes du gennemgå følgende dele af min tidligere artikel om testdrevet databaseudvikling (TDDD):

  • Gå til Start Test-Driven Database Development (TDDD) – Del 1
  • Hop til Start Test-Driven Database Development (TDDD) – Del 2
  • Hop til Start Test-Driven Database Development (TDDD) – Del 3

Ting at gøre

Du kan nu oprette databaseenhedstest til lidt komplekse scenarier, hvor lagrede procedurer kalder hjælpeprocedurer.

  1. Prøv venligst at ændre spionproceduren @CommandToExecute argument (værdi) som @CommandToExecute ='set @DayType ="Intet" ' og se, at testen vil mislykkes nu
  2. Prøv venligst at opfylde forretningskravet i denne artikel ved at bruge testdrevet databaseudvikling (TDDD)
  3. Prøv venligst at opfylde et andet forretningskrav for at se den seneste ordre afgivet af enhver kunde, der bruger testdrevet udvikling (TDDD), der involverer den samme hjælpeprocedure
  4. Prøv venligst at oprette en enhedstest for hjælpeproceduren ved at isolere hovedproceduren
  5. Prøv dig selv at oprette en enhedstest for en procedure, der kalder to hjælpeprocedurer

Nyttigt værktøj:

dbForge Unit Test – en intuitiv og praktisk GUI til implementering af automatiseret enhedstest i SQL Server Management Studio.


  1. Regulært udtryk find og erstat i Postgres

  2. EM 12c Justering af tærskelværdier

  3. Sådan forhindrer du SQL-injektionsangreb med Secure

  4. Hvad er formålet med at bruge OPTION(MAXDOP 1) i SQL Server?