sql >> Databasteknik >  >> RDS >> Database

Hoppa till Start Testdriven Databas Development (TDDD)

Som regel börjar vi utveckla databaslösningar genom att skapa databasobjekt, såsom tabeller, vyer, lagrade procedurer etc. utifrån affärskrav. Detta tillvägagångssätt är också känt som konventionell databasutveckling . I den här artikeln ska vi utforska detta tillvägagångssätt och illustrera det med exempel.

Konventionell databasutveckling

Utvecklingsstilen består av följande steg:

  1. Ta emot kraven
  2. Skapa databasobjekt baserat på krav
  3. Kör enhetstester för databasobjekt för att se om de uppfyller kraven
  4. Ta emot nya krav
  5. Ändra befintliga databasobjekt eller lägg till nya för att möta de nya kraven
  6. Skapa och kör enhetstester för att kontrollera om nya krav fungerar i enlighet med detta och inte står i konflikt med de tidigare

För att utforska och illustrera processerna, låt oss börja med att skapa en exempeldatabas SQLDevBlog :

 
-- Create sample database (SQLDevBlog)
  CREATE DATABASE SQLDevBlog

Använd följande kod för att skapa tabeller i den exempeldatabasen:

USE SQLDevBlog;

-- (1) Create Author table in the sample database
CREATE TABLE Author (
  AuthorId INT PRIMARY KEY IDENTITY (1, 1)
 ,Name VARCHAR(40)
 ,RegistrationDate DATETIME2
 ,Notes VARCHAR(400)
)

-- (2) Create Article Category table in the sample database
CREATE TABLE Category (
  CategoryId INT PRIMARY KEY IDENTITY (1, 1)
 ,Name VARCHAR(50)
 ,Notes VARCHAR(400)
)

-- (3) Create Article table in the sample database
CREATE TABLE Article (
  ArticleId INT PRIMARY KEY IDENTITY (1, 1)
 ,CategoryId INT
 ,AuthorId INT
 ,Title VARCHAR(150)
 ,Published DATETIME2
 ,Notes VARCHAR(400)  
)

-- Adding foreign keys for author and article category
ALTER TABLE Article ADD CONSTRAINT FK_Category_CategoryId FOREIGN KEY (CategoryId) REFERENCES Category (CategoryId)
ALTER TABLE Article ADD CONSTRAINT FK_Author_AuthorId FOREIGN KEY (AuthorId) REFERENCES Author (AuthorId)

GO

Granska databasdiagrammet med våra nyskapade tabeller:

Obs :Jag använder här dbForge Studio för SQL Server för att göra alla uppgifter. Utseendet på dess utdata kan skilja sig från SSMS (SQL Server Management Studio), men resultaten är desamma.

Därefter kommer vi att fylla i vår SQLDevBlogg exempeldatabas för att skapa ett mer realistiskt scenario:

-- (5) Populating Author table
INSERT INTO Author (Name, RegistrationDate, Notes)
  VALUES ('Sam', '2017-01-01', 'Database Analyst'),
  ('Asif', '2017-01-02', 'Database and Business Intelligence Developer'),
  ('Sadaf', '2018-01-01', 'Database Analyst Programmer')

-- (6) Populating Category table
INSERT INTO Category (Name, Notes)
  VALUES ('Development', 'Articles about database development'),
  ('Testing', 'Database testing related articles'),
  ('DLM', 'Database lifecycle management')

-- (7) Populating Article 
INSERT INTO Article (CategoryId, AuthorId, Title, Published, Notes)
  VALUES (1, 1, 'Fundamentals of SQL Database Development', '02-01-2018', ''),
  (1, 2, 'Advanced Database Development', '02-01-2018', ''),
  (2, 3, 'All About Database Testing', '03-01-2018', '');
GO

Som ett resultat har vi följande ifyllda tabeller:

Nu när vi har gjort med databasinställning och tabellpopulation står vi inför nästa steg. Vi måste imitera scenariot med ett nytt krav.

Krav för att lägga till en ny kategori

Ett nytt krav anger att en administratör ska kunna lägga till en ny kategori i listan över tillgängliga kategorier . För att uppfylla detta krav måste ditt utvecklingsteam komma med en lagrad procedur för att enkelt lägga till ett nytt krav. Eller så måste vi skapa en AddCategory Databasobjekt.

För att skapa den lagrade proceduren, kör följande skript:

-- (8) This procedure meets a new requirement by adding a new category
CREATE PROCEDURE dbo.AddCategory @CategoryName VARCHAR(50),
@Notes VARCHAR(400)
AS
  INSERT INTO Category (Name, Notes)
    VALUES (@CategoryName, @Notes);
GO

Resultatet är följande:

Skapa databasenhetstest för att kontrollera om proceduren fungerar korrekt

Nästa steg är att skapa databasenhetstestet för att kontrollera om den lagrade proceduren uppfyller specifikationen.

Det här tipset fungerar för dbForge Studio för SQL Server (eller bara ). dbForge Unit Test ) och SSMS (SQL Server Management Studio) . Obs! När du använder SSMS (SQL Server Management Studio), se till att installera tSQLt Ram för att skriva enhetstesten.

För att skapa det första databasenhetstestet, högerklicka på SQLDevBlog databas> Enhetstest > Lägg till nytt test

Lägg till nytt test fönstret öppnas. Fyll i all nödvändig information och klicka på Lägg till test .

Skapa enhetstestet enligt följande och spara det:

--  Comments here are associated with the test.
--  For test case examples, see: http://tsqlt.org/user-guide/tsqlt-tutorial/
CREATE PROCEDURE AddCategoryTests.[test to check if AddCategory procedure works]
AS
BEGIN
  --Assemble
  EXEC tSQLt.FakeTable @TableName = 'dbo.Category' -- create an empty dependency free Category table
                    

  
  CREATE TABLE AddCategoryTests.Expected ( -- create expected table 
  CategoryId INT 
 ,Name VARCHAR(50) NULL
 ,Notes VARCHAR(400) NULL
  ) 
                      
  INSERT INTO AddCategoryTests.Expected (CategoryId,Name, Notes) -- Insert data into expected table
  VALUES (null,'Database Dummy Category', 'This is just a dummy category for testing');
  
  --Act
  EXEC AddCategory @CategoryName = 'Database Dummy Category' 
                  ,@Notes = 'This is just a dummay category for testing'

   
  --Assert
  EXEC tSQLt.AssertEqualsTable @Expected = 'AddCategoryTests.Expected'
                              ,@Actual = 'dbo.Category'
                           

END;
GO

Klicka på Databas meny> Enhetstest > Visa testlista och kör enhetstestet enligt nedan:

Vi kan se att enhetstestet är framgångsrikt. Således kan en ny kategori läggas till i databasen (tabell). Kravet är uppfyllt.

Nu ska vi utforska testdriven databasutveckling och beskriva hur processen att skriva enhetstester kan uppfylla kraven.

Testdriven databasutveckling (TDDD)

Testdriven databasutveckling (TDDD) börjar med att skriva enhetstestet som misslyckas först. Sedan ändrar vi den så att den godkänns och förfinar den sedan.

Enhetstester är skrivna för att uppfylla krav och enhetstester som kräver att databasobjekt skapas och körs korrekt.

För att förstå skillnaden mellan traditionell databasutveckling och testdriven databasutveckling, låt oss skapa SQLDevBlogTDD databas. Det är samma sak som SQLDevBlog .

-- Create sample database (SQLDevBlogTDD)
  CREATE DATABASE SQLDevBlogTDD

Fyll sedan i exempeldatabasen med tabeller:

USE SQLDevBlogTDD;

-- (1) Create Author table in the sample database
CREATE TABLE Author (
  AuthorId INT PRIMARY KEY IDENTITY (1, 1)
 ,Name VARCHAR(40)
 ,RegistrationDate DATETIME2
 ,Notes VARCHAR(400)
)

-- (2) Create Article Category table in the sample database
CREATE TABLE Category (
  CategoryId INT PRIMARY KEY IDENTITY (1, 1)
 ,Name VARCHAR(50)
 ,Notes VARCHAR(400)
)

-- (3) Create Article table in the sample database
CREATE TABLE Article (
  ArticleId INT PRIMARY KEY IDENTITY (1, 1)
 ,CategoryId INT
 ,AuthorId INT
 ,Title VARCHAR(150)
 ,Published DATETIME2
 ,Notes VARCHAR(400)  
)

-- Adding foreign keys for author and article category
ALTER TABLE Article ADD CONSTRAINT FK_Category_CategoryId FOREIGN KEY (CategoryId) REFERENCES Category (CategoryId)
ALTER TABLE Article ADD CONSTRAINT FK_Author_AuthorId FOREIGN KEY (AuthorId) REFERENCES Author (AuthorId)

GO

Vi måste fylla i vår exempeldatabas för att skapa ett mer realistiskt scenario enligt följande:

-- Use SQLDevBlogTDD
-- (5) Populating Author table
INSERT INTO Author (Name, RegistrationDate, Notes)
  VALUES ('Sam', '2017-01-01', 'Database Analyst'),
  ('Asif', '2017-01-02', 'Database and Business Intelligence Developer'),
  ('Sadaf', '2018-01-01', 'Database Analyst Programmer')

-- (6) Populating Category table
INSERT INTO Category (Name, Notes)
  VALUES ('Development', 'Articles about database development'),
  ('Testing', 'Database testing related articles'),
  ('DLM', 'Database lifecycle management')

-- (7) Populating Article 
INSERT INTO Article (CategoryId, AuthorId, Title, Published, Notes)
  VALUES (1, 1, 'Fundamentals of SQL Database Development', '02-01-2018', ''),
  (1, 2, 'Advanced Database Development', '02-01-2018', ''),
  (2, 3, 'All About Database Testing', '03-01-2018', '');
GO

Krav för att lägga till ny kategori (TDDD)

Nu har vi samma krav:administratören ska kunna lägga till en ny kategori till listan över tillgängliga kategorier. För att uppfylla kravet måste vi först skriva ett databasenhetstest som letar efter ett potentiellt objekt.

Det är så TDDD fungerar:enhetstestet misslyckas först eftersom vi antar att vi letar efter objektet som för närvarande inte finns, men det kommer snart att finnas där.

Skapa och kör databasenhetstestet för att kontrollera att det önskade objektet finns

Även om vi vet att det inte finns nu, tänker vi på detta som en utgångspunkt.

I dbForge Studio för SQL Server skapas databasenhetstestet som stöder TDDD som standard för att misslyckas först. Sedan ska vi ändra på det lite. Om du använder en tSQLt databasenhetstestramverket direkt, skriv följande enhetstest:

--  Comments here are associated with the test.
--  For test case examples, see: http://tsqlt.org/user-guide/tsqlt-tutorial/
CREATE PROCEDURE CategoryTests.[test to check if routine to add new category exists]
AS
BEGIN
  --Assemble
  --  This section is for code that sets up the environment. It often
  --  contains calls to methods such as tSQLt.FakeTable and tSQLt.SpyProcedure
  --  along with INSERTs of relevant data.
  --  For more information, see http://tsqlt.org/user-guide/isolating-dependencies/

  --Act
  --  Execute the code under tests like a stored procedure, function, or view
  --  and capture the results in variables or tables.

  --Assert
  --  Compare the expected and actual values, or call tSQLt.Fail in an IF statement.
  --  Available Asserts: tSQLt.AssertEquals, tSQLt.AssertEqualsString, tSQLt.AssertEqualsTable
  --  For a complete list, see: http://tsqlt.org/user-guide/assertions/
  EXEC tSQLt.AssertObjectExists @ObjectName = N'dbo.AddCategory'
                              

END;
GO

När du har kört databasenhetstestet kan du se att testet misslyckas:

Skapa databasobjektet och kör enhetstestet igen

Nästa steg är att skapa det nödvändiga databasobjektet. I vårt fall är det en lagrad procedur.

-- (8) This procedure meets the new requirement by adding a new category
CREATE PROCEDURE dbo.AddCategory @CategoryName VARCHAR(50),
@Notes VARCHAR(400)
AS  
-- Category Procedure Stub (template) in TDDD
GO

Kör enhetstestet igen – den här gången lyckas det:

Men det räcker inte att klara enhetstestkontrollen om den lagrade proceduren existerar. Vi måste också kontrollera om den lagrade proceduren lägger till en ny kategori.

Skapa databasenhetstestet för att kontrollera om rutinen fungerar korrekt

Låt oss skapa det nya databasenhetstestet:

--  Comments here are associated with the test.
--  For test case examples, see: http://tsqlt.org/user-guide/tsqlt-tutorial/
CREATE PROCEDURE CategoryTests.[test to check routine adds new category]
AS
BEGIN
  --Assemble
  EXEC tSQLt.FakeTable @TableName = 'dbo.Category' -- create an empty dependency free Category table
                      

  
  CREATE TABLE CategoryTests.Expected ( -- create expected table 
  CategoryId INT 
 ,Name VARCHAR(50) NULL
 ,Notes VARCHAR(400) NULL
  ) 
                      
  INSERT INTO CategoryTests.Expected (CategoryId,Name, Notes) -- Insert data into expected table
  VALUES (null,'Database Dummy Category', 'This is just a dummy category for testing');
  
  --Act
  EXEC AddCategory @CategoryName = 'Database Dummy Category' 
                  ,@Notes = 'This is just a dummay category for testing'

  --SELECT * INTO CategoryTests.Actual FROM Category -- put category table data into an actual table
  
  --Assert
  EXEC tSQLt.AssertEqualsTable @Expected = 'CategoryTests.Expected'
                              ,@Actual = 'dbo.Category'
                           

END;
GO

Som du kan se misslyckas enhetstestet för första gången och lyckas för andra gången:

Lägg till funktionalitet i rutintestet och testet på nytt

Ändra lagrad procedur genom att lägga till nödvändig funktionalitet så att testet kan lyckas enligt nedan:

-- (8) This procedure meets the new requirement by adding a new category
ALTER PROCEDURE dbo.AddCategory @CategoryName VARCHAR(50),
@Notes VARCHAR(400)
AS
  INSERT INTO Category (Name, Notes)
    VALUES (@CategoryName, @Notes);
GO

Kör enhetstesten igen för att kontrollera att de alla lyckas, inklusive den nyligen ändrade lagrade proceduren:

På så sätt har vi framgångsrikt implementerat testdriven databasutveckling. Nu kan vi bara fokusera på kraven. Enhetstesten kapslar in krav och kräver därigenom att databasobjekt skapas och körs korrekt för att uppfylla specifikationen.

Låt oss se hur effektivt TDDD är när det gäller att uppfylla ett krav på affärsrapportering.

Tillfredsställa affärsrapporteringskrav genom TDDD

Vi antar att databasen redan har de nödvändiga objekten (som t.ex. tabeller) innan vi fick det nya kravet på affärsrapportering.

Låt oss skapa en exempeldatabas som heter SQLDevBlogReportTDD :

-- Create sample database (SQLDevBlogReportTDD)
CREATE DATABASE SQLDevBlogReportTDD;
GO

Skapa och fyll sedan i tabellerna för exempeldatabasen med följande kod:

USE SQLDevBlogReportTDD;
-- (1) Create Author table in the sample database
CREATE TABLE Author (
  AuthorId INT PRIMARY KEY IDENTITY (1, 1)
 ,Name VARCHAR(40)
 ,RegistrationDate DATETIME2
 ,Notes VARCHAR(400)
)

-- (2) Create an Article Category table in the sample database
CREATE TABLE Category (
  CategoryId INT PRIMARY KEY IDENTITY (1, 1)
 ,Name VARCHAR(50)
 ,Notes VARCHAR(400)
)

-- (3) Create Article table in the sample database
CREATE TABLE Article (
  ArticleId INT PRIMARY KEY IDENTITY (1, 1)
 ,CategoryId INT
 ,AuthorId INT
 ,Title VARCHAR(150)
 ,Published DATETIME2
 ,Notes VARCHAR(400)  
)

-- Adding foreign keys for author and article category
ALTER TABLE Article ADD CONSTRAINT FK_Category_CategoryId FOREIGN KEY (CategoryId) REFERENCES Category (CategoryId)
ALTER TABLE Article ADD CONSTRAINT FK_Author_AuthorId FOREIGN KEY (AuthorId) REFERENCES Author (AuthorId)

GO

-- (4) Populating Author table
INSERT INTO Author (Name, RegistrationDate, Notes)
  VALUES ('Peter', '2017-01-01', 'Database Analyst'),
  ('Adil', '2017-01-02', 'Database and Business Intelligence Developer'),
  ('Sarah', '2018-01-01', 'Database Analyst Programmer'),
  ('Asim', '2018-01-01', 'Database Analyst')

-- (5) Populating Category table
INSERT INTO Category (Name, Notes)
  VALUES 
  ('Analysis', 'Database Analysis'),
  ('Development', 'Articles about database development'),
  ('Testing', 'Database testing related articles'),
  ('DLM', 'Database lifecycle management')
 

-- (6) Populating Article 
INSERT INTO Article (CategoryId, AuthorId, Title, Published, Notes)
  VALUES (1, 1, 'Replicating a problem in SQL', '02-01-2018', ''),
  (1, 2, 'Modern Database Development Tools', '02-01-2018', ''),
  (3, 3, 'Test Driven Database Development (TDDD)', '03-01-2018', ''),
  (3, 1, 'Database Unit Testing Fundamentals', '10-01-2018', ''),
  (3, 3, 'Unit Testing with tSQLt', '10-01-2018', '')
GO

Skapa en vy för att se listan över alla författare, artiklar och artiklars kategorier:

-- (7) Create a view to see a list of authors, articles, and categories
CREATE VIEW dbo.vwAuthors 
AS SELECT a.Name AS AuthorName,a1.Title AS ArticleTitle,c.Name AS CategoryName  FROM Author a INNER JOIN Article a1 ON a.AuthorId = a1.AuthorId INNER JOIN Category c ON a1.CategoryId = c.CategoryId
GO

Kör vyn av den skapade tabellen för att se resultaten:

Efter att ha bearbetat databasinställningarna och fyllt i tabeller är nästa steg att imitera scenariot där vi fick ett nytt krav.

Affärskrav:Totalt antal artiklar per författarerapport

Överväg ett nytt krav på företagsrapportering. Den måste ange en databasrapport för att se det totala antalet artiklar per författare.

Det första är att tilldela ett databasobjekt som kan uppfylla affärskrav. I vårt fall är det ArticlesPerAuthorReport databasobjekt.

För att uppfylla kravet är det nödvändigt att skapa ett databasenhetstest som letar efter ett potentiellt lämpligt objekt. Som vi vet kommer det här testet att misslyckas först eftersom det kommer att leta efter objektet som inte finns för tillfället men kommer att finnas där snart.

TDDD-implementeringsplan

Enligt standarderna för testdriven databasenhetstestning måste följande saker finnas där för att uppfylla rapporteringskravet:

  1. Utveckla ett enda databasobjekt som uppfyller rapporteringskravet.
  2. Skapa ett enhetstest för att kontrollera objektets existens.
  3. Skapa en objektstubb (platshållare) för att klara det första testet.
  4. Skapa ett andra enhetstest för att kontrollera om objektet matar ut korrekt data i tabellen med korrekt inmatning.
  5. Ändra objektdefinitionen så att det andra testet går igenom.

Skapa databasenhetstestet för att kontrollera att önskat objekt finns

Vi tilldelar ett databasobjekt som kan uppfylla affärskravet. I vårt fall är det ArticlesPerAuthorReport databasobjekt. Nästa steg är att skapa ett databasenhetstest.

För att skapa det första databasenhetstestet, högerklicka på SQLDevBlogReport databas> Enhetstest > Lägg till nytt test

Skriv följande kod för att skapa enhetstestet som kontrollerar existensen eller frånvaron av det önskade objektet:

CREATE PROCEDURE ArticlesPerAuthorReport.[test to check ArticlesPerAuthorReport exists]
AS
BEGIN
  --Assemble
 
  --Act
  
  --Assert
   EXEC tSQLt.AssertObjectExists @ObjectName = N'ArticlesPerAuthorReport'
END;
GO

Enhetstestet måste misslyckas eftersom det letar efter objektet som skapats tidigare. Själva objektet är skapat för att följa TDDD:

Skapa Object Stub och kör enheten

Skapa en objektstubb med någon hårdkodad förväntad utdata, eftersom vi bara vill skapa ett databasobjekt med det förväntade resultatet. Skapa ArticlesPerAuthorReport objekt som en vystub (platshållare) först:

-- (8) Create ArticlesPerAuthorReport view stub
  CREATE VIEW ArticlesPerAuthorReport
    AS
    SELECT 'Adil' AS Author, 10 AS [Total Articles]
    UNION ALL
    SELECT 'Sam' AS Author, 5 AS [Total Articles]

Att köra enhetstestet bör lyckas:

Att skapa en stubb fungerar som en kickstart för TDDD. Vi skapar objektet för att klara testet och bryr oss inte om objektets faktiska funktion.

Skapa och kör enhetstestet för att kontrollera om objekt matar ut korrekta data

Det är dags att kontrollera om det önskade objektet fungerar korrekt. Skapa ytterligare ett enhetstest för att kontrollera data som matas ut av det önskade objektet (ArticlesPerAuthorReport ). Låt oss lägga till ett nytt enhetstest till databasen:

Lägg till följande enhetstestkod:

CREATE PROCEDURE ArticlesPerAuthorReport.[test to check ArticlesPerAuthorReport outputs correct]
AS
BEGIN
  --Assemble
  --  Create mocked up tables (blank copies of original tables without constraints and data)
  EXEC tSQLt.FakeTable @TableName = N'Author'
                      ,@SchemaName = N'dbo'

  EXEC tSQLt.FakeTable @TableName = N'Article'
                      ,@SchemaName = N'dbo'                      

  EXEC tSQLt.FakeTable @TableName = N'Category'
                      ,@SchemaName = N'dbo'                      

  -- Add rows to the mocked up tables
  INSERT INTO Author (AuthorId,Name, RegistrationDate, Notes)
  VALUES (1,'Zak', DATEFROMPARTS(2017,01,01), 'Database Author'),
    (2,'Akeel',DATEFROMPARTS(2018,01,01),'Business Intelligence Author')
  
  INSERT INTO Category (CategoryID,Name, Notes)
  VALUES (1,'Database Development', '-'),
  (2,'Business Intelligene','-');

  INSERT INTO Article (ArticleId,CategoryId, AuthorId, Title, Published, Notes)
  VALUES (1,1, 1, 'Advanced Database Development', DATEFROMPARTS(2017,02,01),'10K Views'),
  (1,1, 1, 'Database Development with Cloud Technologies', DATEFROMPARTS(2017,02,01),'5K Views'),
  (1,1, 1, 'Developing Databases with Modern Tools', DATEFROMPARTS(2017,03,01),'20K Views'),
  (1,2, 2, 'Business Intelligence Fundamentals', DATEFROMPARTS(2017,02,01),'10K Views'),
  (1,2, 2, 'Tabular Models', DATEFROMPARTS(2017,02,01),'50K Views')

  -- Create an expected table
  CREATE TABLE ArticlesPerAuthorReport.Expected
  (Author VARCHAR(40),[Total Articles] int)  

  -- Add expected results into an expected table
  INSERT INTO ArticlesPerAuthorReport.Expected (Author, [Total Articles])
  VALUES ('Zak', 3), ('Akeel',2);


  --Act
  --  Run ArticlesPerAuthorReport object (view) and put results into an actual table
  SELECT * INTO ArticlesPerAuthorReport.Actual FROM ArticlesPerAuthorReport apar
  

  --Assert
  --  Compare the expected and actual tables
  EXEC TSQLT.AssertEqualsTable @Expected = N'ArticlesPerAuthorReport.Expected'
                              ,@Actual = N'ArticlesPerAuthorReport.Actual'
                              

END;
GO

Kör enhetstestet som också måste misslyckas för att uppfylla TDDD:

Lägg till nödvändig funktionalitet till objektet ArticlesPerAuthorReport

Enhetstestet som kontrollerar objektets funktionalitet kräver en modifierad struktur så att testet kan klara.

Ändra ArticlesPerAuthorReport visa för att låta det andra enhetstestet passera precis som det första:

ALTER VIEW ArticlesPerAuthorReport
  AS
SELECT a.Name AS [Author],COUNT(a1.ArticleId) AS [Total Articles] FROM Author a 
    INNER JOIN Article a1 ON a.AuthorId = a1.AuthorId
    GROUP BY a.Name

Databasobjektet har framgångsrikt modifierats för att mata ut önskad data. Kör alla enhetstester:

ArticlesPerAuthorReport objektet är klart.

Vår nästa uppgift är att ge en genomgång av att skapa en rapportbas på ett databasobjekt utvecklat och testat med hjälp av testdriven utveckling (TDDD).

Implementera rapporteringskrav (ArticlesPerAuthorReport)

Först ska vi återställa SQLDevBlogReportTDD och lägg till mer data till den. Eller så kan du skapa en tom databas för första gången.

För att lägga till tillräckligt med data till vår exempeldatabas, hämta data från vwAuthors visa för att se alla poster:

Köra enhetstesterna

Kör databasenhetstesten som vi skapade tidigare:

Grattis, båda testerna har godkänts vilket innebär att det önskade databasobjektet kan uppfylla rapporteringskravet för att se artiklar per författare.

Skapa databasrapport baserad på ArticlesPerAuthorsReport Object

Du kan skapa en databasrapport på många sätt (som att använda Report Builder, skapa Report Server Project i Visual Studio Data Tools eller använda dbForge Studio för SQL Server).

I det här avsnittet använder vi dbForge Studio för SQL Server för att skapa rapport. Klicka på Ny för att fortsätta från filen Meny> Datarapport :

Klicka på Standardrapport :

Välj Enkel tabell\vy som Datatyp :

Lägg till basobjektet (ArticlesPerAuthorReport ), vilket är en uppfattning i vårt fall:

Lägg till de obligatoriska fälten:

Vi behöver ingen gruppering just nu, så fortsätt genom att klicka på Nästa :

Välj Layout och Orientering i datarapporten:

Lägg slutligen till rubriken Artiklar per författarerapport och klicka på Slutför :

Justera sedan formateringen av rapporten enligt kraven:

Klicka på Förhandsgranska för att se databasrapporten:

Spara rapporten som ArticlesPerAuthorReport . Databasrapporten har skapats på grund av affärskraven.

Användning av installationsproceduren

När du skriver databasenhetstester med tSQLt kommer du att se att viss testkod upprepas ofta. Således kan du definiera den i en inställningsprocedur och återanvända den i efterhand i andra enhetstester av den specifika testklassen. Varje testklass kan bara ha en inställningsprocedur som körs automatiskt innan enhetstesten av den klassen processer.

Vi kan lägga nästan all testkod skriven under Sammanställ (avsnitt) under installationsproceduren för att undvika kodduplicering.

Till exempel måste vi skapa hånade tabeller tillsammans med en förväntad tabell. Sedan lägger vi till data till hånade tabeller och sedan till den förväntade tabellen. Vi kan enkelt definiera det under installationsproceduren och återanvända det ytterligare.

Skapa installationsprocedur för att undvika duplicering av testkod

Skapa en lagrad procedur i SQLDevBlogTDD enligt följande:

-- (12) Use of Setup Procedure to avoid repeating common test code
CREATE PROCEDURE ArticlesPerAuthorReport.Setup 
AS 
BEGIN
  --Assemble
  --  Create mocked up tables (blank copies of original tables without constraints and data)
  EXEC tSQLt.FakeTable @TableName = N'Author'
                      ,@SchemaName = N'dbo'

  EXEC tSQLt.FakeTable @TableName = N'Article'
                      ,@SchemaName = N'dbo'                      

  EXEC tSQLt.FakeTable @TableName = N'Category'
                      ,@SchemaName = N'dbo'                      

  -- Add rows to the mocked up tables
  INSERT INTO Author (AuthorId,Name, RegistrationDate, Notes)
  VALUES (1,'Zak', DATEFROMPARTS(2017,01,01), 'Database Author'),
    (2,'Akeel',DATEFROMPARTS(2018,01,01),'Business Intelligence Author')
  
  INSERT INTO Category (CategoryID,Name, Notes)
  VALUES (1,'Database Development', '-'),
  (2,'Business Intelligene','-');

  INSERT INTO Article (ArticleId,CategoryId, AuthorId, Title, Published, Notes)
  VALUES (1,1, 1, 'Advanced Database Development', DATEFROMPARTS(2017,02,01),'10K Views'),
  (1,1, 1, 'Database Development with Cloud Technologies', DATEFROMPARTS(2017,02,01),'5K Views'),
  (1,1, 1, 'Developing Databases with Modern Tools', DATEFROMPARTS(2017,03,01),'20K Views'),
  (1,2, 2, 'Business Intelligence Fundamentals', DATEFROMPARTS(2017,02,01),'10K Views'),
  (1,2, 2, 'Tabular Models', DATEFROMPARTS(2017,02,01),'50K Views')

  -- Create an expected table
  CREATE TABLE ArticlesPerAuthorReport.Expected
  (Author VARCHAR(40),[Total Articles] int)  

  -- Add expected results into an expected table
  INSERT INTO ArticlesPerAuthorReport.Expected (Author, [Total Articles])
  VALUES ('Zak', 3), ('Akeel',2)
END;
GO

Ta nu bort testkoden som vi har skrivit i installationsproceduren från det tidigare enhetstestet för att kontrollera ArticlesPerAuthorReport utgångar enligt följande:

-- (11) Create unit test check ArticlesPerAuthorReport outputs correct
ALTER PROCEDURE ArticlesPerAuthorReport.[test to check ArticlesPerAuthorReport outputs correct]
AS
BEGIN
  --Assemble (Test Code written in Setup Procedure)
  -- Create mocked up tables (blank copies of original tables without constraints and data)
  -- Add rows to the mocked up tables
  -- Create an expected table
  -- Add expected results into an expected table
  
  --Act
  --  Run ArticlesPerAuthorReport object (view) and put results into an actual table
  SELECT * INTO ArticlesPerAuthorReport.Actual FROM ArticlesPerAuthorReport apar
  

  --Assert
  --  Compare the expected and actual tables
  EXEC TSQLT.AssertEqualsTable @Expected = N'ArticlesPerAuthorReport.Expected'
                              ,@Actual = N'ArticlesPerAuthorReport.Actual'
                              
END;
GO

Running All Unit Tests to Check Setup Procedure Working

Run the unit tests and see the results:

The unit tests have run successfully despite the fact we are using a setup procedure to run some parts of the test code before these unit tests are running.

Use of Stored Procedures

Next, we’ll focus on creating stored procedures through test-driven database development (TDDD) to meet specific requirements that cannot be fulfilled by using a database view.

Let’s assume that business users want to know the Total number of articles per author for a specified year . The database view can’t meet it because it is not defined at the time of writing the script (exactly the year is going to be desired by the business users).

Thus, it requires a database object with parameter(s) capability and it is exactly the stored procedure.

Let us consider a new business requirement to create the report that shows the total number of articles per author for a specified year . We’ll use the sample database called SQLDevBlogReportTDD that we created earlier.

Run the ArticlesPerAuthorReport view to see the results:

Select the Database Object (AuthorsPerArticleByYearReport)

Name the potential database object as AuthorsPerArticleForYearReport .

As we mentioned above, the database view can meet the reporting requirement despite the absence of the specified year . But this variable means that we need the stored procedure which will pass year as an argument to run the report and show the desired results.

Write and Run the Object Exists Unit Test

As we already know, we need to start with writing the basic unit test to check the existence or absence of the desired object.

To create the first database unit test, right-click the SQLDevBlogReport database> Unit Test > Add New Test

Write the following test code:

CREATE PROCEDURE ArticlesPerAuthorByYearReport.[test to check ArticlesPerAuthorByYearReport exists]

AS

BEGIN

--Assemble

--Act

--Assert

EXEC tSQLt.AssertObjectExists @ObjectName = N'ArticlesPerAuthorByYearReport'

,@Message = N''


END;

GO

Right-click on the database> click View Test List under Unit Test to see the Test List Manager :

Check the ArticlesPerAuthorByYearReport test class and click the run test icon:

This complies with TDDD – the unit test checking if object existence is written before the object is created. So, we expect the test to fail first.

Create Object Stub (dummy object)

We are going to create an object stub that mocks the object’s functionality. At this stage, we only need that object, the desired functionality is out of the question.

Create a stored procedure type object as the stub and call it ArticlesPerAuthorByYearReport by using the following code:

-- Create report object (stored procedure) stub

CREATE PROCEDURE dbo.ArticlesPerAuthorByYearReport

@Year INT

AS

SELECT 'Adil' AS Author, 10 AS [Total Articles], 0000 AS [Year]

UNION ALL

SELECT 'Sam' AS Author, 5 AS [Total Articles], 0000 AS [Year]

GO

After we created the object stub, the basic unit test that checks for the existence of the object will be successful:

Write and Run the Object Functionality Unit Test

To comply with TDDD, we need to write a unit test to check whether the desired object ArticlesPerAuthorByYearReport functions properly. Since the object was created as a stub (placeholder), this unit test is also going to fail first. The object has to function properly yet despite the fact it was created and passed the basic check of its existence.

Create a second unit test to check if the object outputs correct data by creating a setup procedure (which helps us to write shared test code within the same test class) that is followed by the unit test:

CREATE PROCEDURE ArticlesPerAuthorByYearReport. Setup

AS

BEGIN

--Assemble

-- Create mocked up tables (blank copies of original tables without constraints and data)

EXEC tSQLt.FakeTable @TableName = N'Author'

,@SchemaName = N'dbo'




EXEC tSQLt.FakeTable @TableName = N'Article'

,@SchemaName = N'dbo'




EXEC tSQLt.FakeTable @TableName = N'Category'

,@SchemaName = N'dbo'




-- Add rows to the mocked up tables

INSERT INTO Author (AuthorId,Name, RegistrationDate, Notes)

VALUES (1,'Zak', DATEFROMPARTS(2017,01,01), 'Database Author'),

(2,'Akeel',DATEFROMPARTS(2018,01,01),'Business Intelligence Author')

INSERT INTO Category (CategoryID,Name, Notes)

VALUES (1,'Database Development', '-'),

(2,'Business Intelligene','-');




INSERT INTO Article (ArticleId,CategoryId, AuthorId, Title, Published, Notes)

VALUES (1,1, 1, 'Advanced Database Development', DATEFROMPARTS(2017,02,01),'10K Views'),

(1,1, 1, 'Database Development with Cloud Technologies', DATEFROMPARTS(2017,02,01),'5K Views'),

(1,1, 1, 'Developing Databases with Modern Tools', DATEFROMPARTS(2017,03,01),'20K Views'),

(1,2, 2, 'Business Intelligence Fundamentals', DATEFROMPARTS(2016,02,01),'10K Views'),

(1,2, 2, 'Tabular Models', DATEFROMPARTS(2016,02,01),'50K Views')




-- Create an expected table

CREATE TABLE ArticlesPerAuthorByYearReport.Expected

(Author VARCHAR(40),[Total Articles] INT,[Year] INT)




-- Create an actual table

CREATE TABLE ArticlesPerAuthorByYearReport.Actual

(Author VARCHAR(40),[Total Articles] INT,[Year] INT)




-- Add expected results into an expected table for the year 2017

INSERT INTO ArticlesPerAuthorByYearReport.Expected (Author, [Total Articles],[Year])

VALUES ('Zak', 3,2017)




END;

GO

Write the unit test to check if the object functions properly:

-- Create unit test to check ArticlesPerAuthorByYearReport outputs correct data

CREATE PROCEDURE ArticlesPerAuthorByYearReport.[test to check ArticlesPerAuthorByYearReport outputs correct data]

AS

BEGIN

--Assemble (Test Code written in Setup Procedure)

-- Create mocked up tables (blank copies of original tables without constraints and data)

-- Add rows to the mocked up tables

-- Create an expected table

-- Create an actual table

-- Add expected results into an expected table

--Act

-- Call desired object (stored procedure) and put results into an actual table

INSERT INTO ArticlesPerAuthorByYearReport.Actual

EXEC dbo.ArticlesPerAuthorByYearReport @Year=2017




--Assert

-- Compare the expected and actual tables

EXEC TSQLT.AssertEqualsTable @Expected = N'ArticlesPerAuthorByYearReport.Expected'

,@Actual = N'ArticlesPerAuthorByYearReport.Actual'

END;

GO

Run the unit test. As demonstrated earlier, it will fail first since we have not added the desired functionality to the object yet:

Add Object Functionality and Rerun the Unit Test

Add the object functionality by modifying the stored procedure as follows:

-- Create report object (stored procedure) to show articles per author for a specified year

CREATE PROCEDURE dbo.ArticlesPerAuthorByYearReport

@Year INT

AS




SELECT

a.Name AS [Author],COUNT(a1.ArticleId) AS [Total Articles],YEAR(a.RegistrationDate) AS [Year]

FROM Author a

INNER JOIN Article a1

ON a.AuthorId = a1.AuthorId

WHERE YEAR(a.RegistrationDate) = @Year

GROUP BY a.Name,YEAR(a.RegistrationDate)

GO

Note :If you are using a declarative database development tool like dbForge Studio for SQL Server, you’ll use the Create Procedure statement to modify the object. For tools like SSMS (SQL Server Management Studio), you must use ALTER Procedure .

Rerunning the database unit test for checking the proper object functioning gives us the following results:

You have successfully unit tested the reporting procedure that is responsible for meeting the business requirement.

Slutsats

Test-driven database development (TDDD) is a specific approach. To meet the business requirement(s), potential database object(s) must pass the unit test(s) and satisfy the following conditions under normal circumstances:

  • The database object must exist
  • The database object must function properly to meet the business requirement

First, the unit tests have to fail because they are created before the creation of the object/defining the object functionality. After adding the necessary objects and ensuring their functionality, the unit tests succeed.

This article examined the basics of test-driven database development and illustrated it with practical examples. We hope that the article was helpful to you. Feel free to share your opinions and maybe some lifehacks in the Comments section, and stay tuned for the next materials!


  1. ett bättre tillvägagångssätt än att lagra mysql-lösenord i vanlig text i konfigurationsfil?

  2. ORA-12728:ogiltigt intervall i reguljärt uttryck

  3. Hur man skapar kapslade tabeller med användardefinierad datatyp i Oracle Database

  4. Hur man åtgärdar MySQL-felet 1064