sql >> Databáze >  >> RDS >> Database

Jump to Start Test-Driven Database Development (TDDD)

Databázová řešení zpravidla začínáme vyvíjet vytvářením databázových objektů, jako jsou tabulky, pohledy, uložené procedury atd., na základě obchodních požadavků. Tento přístup je také známý jako vývoj konvenční databáze . V tomto článku tento přístup prozkoumáme a ilustrujeme na příkladech.

Vývoj konvenční databáze

Vývojový styl se skládá z následujících kroků:

  1. Přijměte požadavky
  2. Vytvářejte databázové objekty na základě požadavků
  3. Spusťte testy jednotek pro databázové objekty, abyste zjistili, zda splňují požadavky
  4. Získejte nové požadavky
  5. Upravte stávající databázové objekty nebo přidejte nové, aby splňovaly nové požadavky
  6. Vytvářejte a spouštějte testy jednotek, abyste ověřili, zda nové požadavky fungují odpovídajícím způsobem a nejsou v rozporu s předchozími.

Abychom prozkoumali a ilustrovali procesy, začněme nastavením ukázkové databáze SQLDevBlog :

 
-- Create sample database (SQLDevBlog)
  CREATE DATABASE SQLDevBlog

K vytvoření tabulek v této ukázkové databázi použijte následující kód:

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

Prohlédněte si databázový diagram s našimi nově vytvořenými tabulkami:

Poznámka :K provádění všech úkolů zde používám dbForge Studio pro SQL Server. Vzhled jeho výstupu se může lišit od SSMS (SQL Server Management Studio), ale výsledky jsou stejné.

Dále naplníme náš SQLDevBlog ukázková databáze k vytvoření realističtějšího scénáře:

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

V důsledku toho máme následující naplněné tabulky:

Nyní, když jsme skončili s nastavením databáze a populací tabulek, stojíme před dalším krokem. Musíme napodobit scénář s novým požadavkem.

Požadavek na přidání nové kategorie

Nový požadavek stanoví, že administrátor by měl mít možnost přidat novou kategorii do seznamu dostupných kategorií . Aby váš vývojový tým splnil tento požadavek, musí přijít s uloženou procedurou pro snadné přidání nového požadavku. Nebo musíme vytvořit AddCategory Objekt databáze.

Chcete-li vytvořit uloženou proceduru, spusťte následující 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

Výsledek je následující:

Vytvořte test databázové jednotky a zkontrolujte, zda postup funguje správně

Dalším krokem je vytvoření testu databázové jednotky pro kontrolu, zda uložená procedura splňuje specifikaci.

Tento tip funguje pro dbForge Studio pro SQL Server (nebo pouze Test jednotky dbForge ) a SSMS (SQL Server Management Studio) . Poznámka:Pokud používáte SSMS (SQL Server Management Studio), nezapomeňte nainstalovat tSQLt Framework pro psaní jednotkových testů.

Chcete-li vytvořit první test databázové jednotky, klikněte pravým tlačítkem na SQLDevBlog databáze> Test jednotky > Přidat nový test

Přidat nový test otevře se okno. Vyplňte všechny požadované informace a klikněte na Přidat test .

Vytvořte test jednotky následovně a uložte jej:

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

Klikněte na Databázi menu> Test jednotky > Zobrazit seznam testů a spusťte test jednotky, jak je uvedeno níže:

Vidíme, že test jednotky je úspěšný. Do databáze (tabulky) tak lze přidat novou kategorii. Požadavek byl splněn.

Nyní prozkoumáme vývoj databází řízený testy a popíšeme, jak může proces psaní jednotkových testů uspokojit požadavky.

Test-Driven Database Development (TDDD)

Testem řízený vývoj databáze (TDDD) začíná napsáním testu jednotky, který selže jako první. Poté jej upravíme tak, aby vyhovoval, a poté jej upřesníme.

Unit testy jsou napsány tak, aby splňovaly požadavky a unit testy, které vyžadují vytvoření a správné fungování databázových objektů.

Abychom pochopili rozdíl mezi tradičním vývojem databází a vývojem databází řízeným testováním, vytvořte SQLDevBlogTDD databáze. Je to stejné jako SQLDevBlog .

-- Create sample database (SQLDevBlogTDD)
  CREATE DATABASE SQLDevBlogTDD

Potom naplňte ukázkovou databázi tabulkami:

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

Musíme naplnit naši ukázkovou databázi, abychom vytvořili realističtější scénář následovně:

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

Požadavek na přidání nové kategorie (TDDD)

Nyní máme stejný požadavek:administrátor by měl mít možnost přidat novou kategorii do seznamu dostupných kategorií. Abychom splnili požadavek, musíme nejprve napsat test databázové jednotky, který hledá potenciální objekt.

Takto funguje TDDD:Unit test nejprve selže, protože předpokládáme, že hledáme objekt, který momentálně neexistuje, ale brzy tam bude.

Vytvořte a spusťte test databázové jednotky pro kontrolu existence požadovaného objektu

Ačkoli víme, že nyní neexistuje, považujeme to za výchozí bod.

V dbForge Studio pro SQL Server se nejprve vytvoří test databázové jednotky, který standardně podporuje TDDD. Pak to trochu změníme. Pokud používáte tSQLt přímo rámec pro testování databázových jednotek, napište následující test jednotek:

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

Po spuštění testu databázové jednotky můžete vidět, že test selhal:

Vytvořte objekt databáze a znovu spusťte test jednotky

Dalším krokem je vytvoření požadovaného databázového objektu. V našem případě se jedná o uloženou proceduru.

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

Spusťte znovu test jednotky – tentokrát bude úspěšný:

Nestačí však projít testem jednotky, který kontroluje, zda uložená procedura existuje. Musíme také zkontrolovat, zda uložená procedura přidává novou kategorii.

Vytvořte test databázové jednotky, abyste ověřili, zda rutina funguje správně

Pojďme vytvořit nový test databázové jednotky:

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

Jak vidíte, test jednotky selže napoprvé a napodruhé uspěje:

Přidat funkcionalitu do rutinního testu a znovu spustit test jednotky

Upravte uloženou proceduru přidáním požadované funkce tak, aby byl test úspěšný, jak je uvedeno níže:

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

Znovu spusťte testy jednotek a zkontrolujte, zda jsou všechny úspěšné, včetně nedávno upravené uložené procedury:

Tímto způsobem jsme úspěšně implementovali vývoj databází řízený testováním. Nyní se můžeme soustředit pouze na požadavky. Unit testy zapouzdřují požadavky, a proto vyžadují vytvoření a správné fungování databázových objektů, aby splňovaly specifikaci.

Podívejme se, jak efektivní je TDDD, pokud jde o uspokojování požadavků na obchodní výkaznictví.

Splnění požadavku obchodního výkaznictví prostřednictvím TDDD

Předpokládáme, že databáze již získala potřebné objekty (jako jsou tabulky) před přijetím nového požadavku na obchodní výkaznictví.

Vytvořme ukázkovou databázi s názvem SQLDevBlogReportTDD :

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

Potom vytvořte a naplňte tabulky pro ukázkovou databázi pomocí následujícího kódu:

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

Vytvořte zobrazení pro zobrazení seznamu všech autorů, článků a kategorií článků:

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

Spusťte zobrazení vytvořené tabulky, abyste viděli výsledky:

Po zpracování nastavení databáze a naplnění tabulek je dalším krokem napodobit scénář, kdy jsme obdrželi nový požadavek.

Obchodní požadavek:Celkový počet článků na zprávu autora

Zvažte nový požadavek na obchodní výkaznictví. Pro zobrazení celkového počtu článků na autora musí být uvedena databázová zpráva.

První věcí je přiřadit databázový objekt, který může splňovat obchodní požadavky. V našem případě je to ArticlesPerAuthorReport databázový objekt.

Pro splnění požadavku je nutné vytvořit test databázové jednotky, který hledá potenciálně vhodný objekt. Jak víme, tento test selže jako první, protože bude hledat objekt, který v tuto chvíli neexistuje, ale brzy tam bude.

Plán implementace TDDD

Podle standardů testování databázových jednotek na základě testů musí být ke splnění požadavku na podávání zpráv splněny následující věci:

  1. Vyviňte jeden databázový objekt, který bude splňovat požadavky na vytváření sestav.
  2. Vytvořte test jednotky pro kontrolu existence objektu.
  3. Vytvořte útržek objektu (zástupný symbol), abyste prošli prvním testem.
  4. Vytvořte test druhé jednotky, abyste zkontrolovali, zda objekt vydává správná data do tabulky se správným vstupem.
  5. Upravte definici objektu, aby druhý test prošel.

Vytvořte test databázové jednotky pro kontrolu existence požadovaného objektu

Přiřadíme databázový objekt, který může splňovat obchodní požadavky. V našem případě je to ArticlesPerAuthorReport databázový objekt. Dalším krokem je vytvoření testu databázové jednotky.

Chcete-li vytvořit první test databázové jednotky, klikněte pravým tlačítkem na SQLDevBlogReport databáze> Test jednotky > Přidat nový test

Napište následující kód pro vytvoření testu jednotky, který zkontroluje existenci nebo nepřítomnost požadovaného objektu:

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

Test jednotky musí selhat, protože kontroluje dříve vytvořený objekt. Samotný objekt je vytvořen tak, aby vyhovoval TDDD:

Vytvořte útržek objektu a spusťte jednotku

Vytvořte útržek objektu s nějakým pevně zakódovaným očekávaným výstupem, protože chceme pouze vytvořit databázový objekt s očekávaným výsledkem. Vytvořte zprávu ArticlesPerAuthorReport objekt jako útržek zobrazení (zástupný symbol):

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

Spuštění testu jednotky by mělo být úspěšné:

Vytvoření pahýlu slouží jako startér pro TDDD. Objekt vytváříme tak, aby prošel testem a nezatěžovali jsme se skutečným fungováním objektu.

Vytvořte a spusťte Unit Test, abyste zkontrolovali, zda objekt vydává správná data

Je čas zkontrolovat, zda požadovaný objekt funguje správně. Vytvořte další test jednotky pro kontrolu výstupu dat požadovaným objektem (ArticlesPerAuthorReport ). Pojďme do databáze přidat nový test jednotek:

Přidejte následující testovací kód jednotky:

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

Spusťte test jednotky, který musí také selhat, aby byl v souladu s TDDD:

Přidat požadovanou funkcionalitu do objektu ArticlesPerAuthorReport

Unit test, který kontroluje funkčnost objektu, vyžaduje upravenou strukturu, aby test prošel.

Upravte zprávu ArticlesPerAuthorReport zobrazení, aby test druhé jednotky prošel stejně jako první:

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

Databázový objekt byl úspěšně upraven pro výstup požadovaných dat. Spusťte všechny testy jednotek:

zpráva ArticlesPerAuthorReport objekt je připraven.

Naším dalším úkolem je poskytnout návod k vytvoření základu sestav na databázovém objektu vyvinutém a testovaném pomocí testem řízeného vývoje (TDDD).

Implementace požadavku na vytváření přehledů (články na zprávu o autorovi)

Nejprve resetujeme SQLDevBlogReportTDD a přidat do něj další data. Nebo můžete poprvé vytvořit prázdnou databázi.

Chcete-li do naší vzorové databáze přidat dostatek dat, načtěte data z vwAuthors zobrazit všechny záznamy:

Spuštění testů jednotek

Spusťte testy databázových jednotek, které jsme vytvořili dříve:

Blahopřejeme, oba testy prošly, což znamená, že požadovaný databázový objekt je schopen splnit požadavek na hlášení pro zobrazení článků podle autora.

Vytvořit zprávu databáze na základě objektu ArticlesPerAuthorsReport

Databázovou sestavu můžete vytvořit mnoha způsoby (například pomocí Report Builderu, vytvořením projektu Report Server v Visual Studio Data Tools nebo pomocí dbForge Studio pro SQL Server).

V této části používáme k vytváření sestav dbForge Studio pro SQL Server. Chcete-li pokračovat, klikněte na Nový ze Souboru Nabídka> Přehled dat :

Klikněte na Standardní přehled :

Vyberte Jednoduchá tabulka\Zobrazení jako Datový typ :

Přidejte základní objekt (ArticlesPerAuthorReport ), což je v našem případě pohled:

Přidejte povinná pole:

V tuto chvíli nepotřebujeme žádné seskupování, takže pokračujte kliknutím na Další :

Vyberte Rozvržení a Orientace datové zprávy:

Nakonec přidejte název Články na zprávu autora a klikněte na Dokončit :

Dále upravte formátování sestavy podle požadavků:

Klikněte na Náhled k zobrazení zprávy databáze:

Uložte přehled jako ArticlesPerAuthorReport . Sestava databáze byla vytvořena kvůli obchodním požadavkům.

Použití postupu nastavení

Když píšete testy databázových jednotek pomocí tSQLt, uvidíte, že se některé testovací kódy často opakují. Můžete jej tedy definovat v proceduře nastavení a poté jej znovu použít v jiných jednotkových testech této konkrétní třídy testu. Každá testovací třída může mít pouze jednu proceduru nastavení, která se automaticky spustí před jednotkovými testy dané třídy.

Můžeme umístit téměř veškerý testovací kód napsaný pod Assemble (část) v rámci postupu nastavení, aby se zabránilo duplicitě kódu.

Potřebujeme například vytvořit simulované tabulky spolu s očekávanou tabulkou. Poté přidáme data do simulovaných tabulek a poté do očekávané tabulky. Můžeme jej snadno definovat v rámci procedury nastavení a znovu použít.

Vytvoření postupu nastavení, aby se zabránilo duplikaci testovacího kódu

Vytvořte uloženou proceduru v SQLDevBlogTDD takto:

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

Nyní odstraňte testovací kód, který jsme napsali v postupu nastavení z předchozího testu jednotky, a zkontrolujte ArticlesPerAuthorReport výstupy takto:

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

Závěr

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. SQLException:Nebyl nalezen žádný vhodný ovladač pro jdbc:oracle:thin:@//localhost:1521/orcl

  2. Používá Oracle vyhodnocování zkratů?

  3. *ALERT* Nelze otevřít žádnou další chybu databází s Microsoft Office build 2201

  4. Jak zastavit/zabít dotaz v postgresql?