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

Vai all'avvio dello sviluppo di database basato su test (TDDD)

Di norma, iniziamo a sviluppare soluzioni di database creando oggetti di database, come tabelle, viste, stored procedure, ecc., in base alle esigenze aziendali. Questo approccio è anche noto come Sviluppo di database convenzionale . In questo articolo esploreremo questo approccio e lo illustreremo con esempi.

Sviluppo database convenzionale

Lo stile di sviluppo consiste nei seguenti passaggi:

  1. Ricevi i requisiti
  2. Crea oggetti database in base ai requisiti
  3. Esegui unit test per gli oggetti del database per vedere se soddisfano i requisiti
  4. Ricevi nuovi requisiti
  5. Modifica gli oggetti del database esistenti o aggiungine di nuovi per soddisfare i nuovi requisiti
  6. Crea ed esegui unit test per verificare se i nuovi requisiti funzionano di conseguenza e non sono in conflitto con quelli precedenti

Per esplorare e illustrare i processi, iniziamo con la creazione di un database di esempio SQLDevBlog :

 
-- Create sample database (SQLDevBlog)
  CREATE DATABASE SQLDevBlog

Utilizzare il codice seguente per creare tabelle nel database di esempio:

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

Esamina il diagramma del database con le nostre tabelle appena create:

Nota :Sto usando qui dbForge Studio per SQL Server per eseguire tutte le attività. L'aspetto del suo output può differire da SSMS (SQL Server Management Studio), ma i risultati sono gli stessi.

Successivamente, compileremo il nostro SQLDevBlog database di esempio per creare uno scenario più realistico:

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

Di conseguenza, abbiamo le seguenti tabelle popolate:

Ora che abbiamo finito con l'impostazione del database e il popolamento delle tabelle, affrontiamo il passaggio successivo. Dobbiamo imitare lo scenario con una nuova esigenza.

Il requisito per aggiungere una nuova categoria

Un nuovo requisito stabilisce che un amministratore dovrebbe essere in grado di aggiungere una nuova categoria all'elenco delle categorie disponibili . Per soddisfare questo requisito, il tuo team di sviluppo deve elaborare una procedura memorizzata per aggiungere facilmente un nuovo requisito. Oppure, dobbiamo creare una AddCategory Oggetto Database.

Per creare la stored procedure, eseguire il seguente script:

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

Il risultato è il seguente:

Crea un test unitario del database per verificare se la procedura funziona correttamente

Il passaggio successivo consiste nel creare lo unit test del database per verificare se la procedura memorizzata soddisfa le specifiche.

Questo suggerimento funziona per dbForge Studio per SQL Server (o solo Test unitario dbForge ) e SSMS (SQL Server Management Studio) . Nota:quando utilizzi SSMS (SQL Server Management Studio), assicurati di installare tSQLt Framework per scrivere gli unit test.

Per creare il primo unit test del database, fare clic con il pulsante destro del mouse su SQLDevBlog database> Unit Test > Aggiungi nuovo test

Aggiungi nuovo test si apre la finestra. Inserisci tutte le informazioni richieste e fai clic su Aggiungi test .

Crea lo unit test come segue e salvalo:

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

Fare clic sul Database menu> Test unità > Visualizza l'elenco dei test ed eseguire lo unit test come mostrato di seguito:

Possiamo vedere che il test unitario ha esito positivo. Pertanto, è possibile aggiungere una nuova categoria al database (tabella). Il requisito è stato soddisfatto.

Ora esploreremo lo sviluppo di database basato su test e descriveremo in che modo il processo di scrittura di unit test può soddisfare i requisiti.

Sviluppo di database basato su test (TDDD)

Lo sviluppo di database basato su test (TDDD) inizia con la scrittura dello unit test che fallirà per primo. Quindi, lo modificheremo per passare e quindi lo perfezioneremo.

Gli unit test vengono scritti per soddisfare i requisiti e gli unit test che richiedono la creazione e l'esecuzione corretta di oggetti di database.

Per comprendere la differenza tra lo sviluppo di database tradizionale e lo sviluppo di database basato su test, creiamo il SQLDevBlogTDD Banca dati. È lo stesso di SQLDevBlog .

-- Create sample database (SQLDevBlogTDD)
  CREATE DATABASE SQLDevBlogTDD

Quindi, popolare il database di esempio con le tabelle:

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

Dobbiamo popolare il nostro database di esempio per creare uno scenario più realistico come segue:

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

Requisito per aggiungere una nuova categoria (TDDD)

Ora abbiamo lo stesso requisito:l'amministratore dovrebbe essere in grado di aggiungere una nuova categoria all'elenco delle categorie disponibili. Per soddisfare il requisito, dobbiamo prima scrivere uno unit test del database che cerchi un potenziale oggetto.

È così che funziona TDDD:lo unit test prima fallisce poiché assumiamo che stiamo cercando l'oggetto che attualmente non esiste, ma sarà lì presto.

Crea ed esegui lo Unit Test del database per verificare l'esistenza dell'oggetto desiderato

Anche se sappiamo che ora non esiste, pensiamo a questo come un punto di partenza.

In dbForge Studio per SQL Server, lo unit test del database che supporta TDDD per impostazione predefinita viene creato per non riuscire prima. Quindi, lo cambieremo un po'. Se stai usando un tSQLt framework di unit test del database direttamente, scrivi il seguente unit test:

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

Dopo aver eseguito lo unit test del database, puoi vedere che il test ha esito negativo:

Crea l'oggetto database ed esegui nuovamente lo unit test

Il passaggio successivo consiste nel creare l'oggetto database richiesto. Nel nostro caso, è una stored procedure.

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

Esegui di nuovo lo unit test, questa volta ha esito positivo:

Ma non è sufficiente superare lo unit test verificando se la stored procedure esiste. Dobbiamo anche verificare se la procedura memorizzata aggiunge una nuova categoria.

Crea il test dell'unità di database per verificare se la routine funziona correttamente

Creiamo il nuovo test unitario del database:

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

Come puoi vedere, lo unit test fallisce per la prima volta e riesce per la seconda volta:

Aggiungi funzionalità alla routine ed esegui nuovamente il test unitario

Modifica la procedura memorizzata aggiungendo la funzionalità richiesta in modo che il test possa avere esito positivo come mostrato di seguito:

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

Eseguire nuovamente gli unit test per verificare che tutti abbiano esito positivo, inclusa la stored procedure modificata di recente:

In questo modo, abbiamo implementato con successo lo sviluppo di database basato su test. Ora possiamo concentrarci solo sui requisiti. Gli unit test incapsulano i requisiti, quindi richiedono la creazione e l'esecuzione corretta di oggetti di database per soddisfare le specifiche.

Vediamo quanto è efficace TDDD quando si tratta di soddisfare un requisito di reporting aziendale.

Soddisfare i requisiti di reporting aziendale tramite TDDD

Presumiamo che il database abbia già gli oggetti necessari (come le tabelle) prima di ricevere il nuovo requisito di reporting aziendale.

Creiamo un database di esempio chiamato SQLDevBlogReportTDD :

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

Quindi creare e popolare le tabelle per il database di esempio utilizzando il codice seguente:

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

Crea una vista per vedere l'elenco di tutti gli autori, articoli e categorie di articoli:

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

Esegui la visualizzazione della tabella creata per vedere i risultati:

Dopo aver elaborato la configurazione del database e popolato le tabelle, il passaggio successivo consiste nell'imitare lo scenario in cui abbiamo ricevuto un nuovo requisito.

Requisito aziendale:numero totale di articoli per rapporto dell'autore

Prendere in considerazione un nuovo requisito di rendicontazione aziendale. Deve indicare un rapporto di database per visualizzare il numero totale di articoli per autore.

La prima cosa è assegnare un oggetto database in grado di soddisfare i requisiti aziendali. Nel nostro caso, è il ArticlesPerAuthorReport oggetto database.

Per soddisfare il requisito, è necessario creare uno unit test di database che cerchi un potenziale oggetto appropriato. Come sappiamo, questo test fallirà prima perché cercherà l'oggetto che al momento non esiste ma sarà lì presto.

Piano di attuazione TDDD

Secondo gli standard del test unitario del database basato su test, per soddisfare i requisiti di segnalazione devono essere presenti i seguenti elementi:

  1. Sviluppare un singolo oggetto database che soddisfi i requisiti di reporting.
  2. Crea uno unit test per verificare l'esistenza dell'oggetto.
  3. Crea un oggetto stub (segnaposto) per superare il primo test.
  4. Crea un secondo unit test per verificare se l'oggetto restituisce i dati corretti nella tabella con l'input corretto.
  5. Modifica la definizione dell'oggetto per far passare il secondo test.

Crea l'unità di test del database per verificare l'esistenza dell'oggetto desiderato

Assegneremo un oggetto database in grado di soddisfare i requisiti aziendali. Nel nostro caso, è il ArticlesPerAuthorReport oggetto di database. Il passaggio successivo consiste nel creare uno unit test del database.

Per creare il primo unit test del database, fare clic con il pulsante destro del mouse su SQLDevBlogReport database> Unit Test > Aggiungi nuovo test

Scrivi il codice seguente per creare lo unit test che verifica l'esistenza o l'assenza dell'oggetto desiderato:

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

Lo unit test deve fallire poiché verifica l'oggetto creato in precedenza. L'oggetto stesso è stato creato per essere conforme a TDDD:

Crea stub oggetto ed esegui l'unità

Crea un oggetto stub con un output previsto hardcoded, poiché vogliamo solo creare un oggetto database con il risultato atteso. Crea il ArticoliPerAutoreReport oggetto inizialmente come vista stub (segnaposto):

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

L'esecuzione dello unit test dovrebbe avere esito positivo:

La creazione di uno stub funge da kick-starter per TDDD. Creiamo l'oggetto per superare il test e non ci preoccupiamo dell'effettivo funzionamento dell'oggetto.

Crea ed esegui lo Unit Test per verificare se l'oggetto emette dati corretti

È ora di verificare se l'oggetto desiderato funziona correttamente. Crea un altro unit test per verificare l'output dei dati dall'oggetto desiderato (ArticlesPerAuthorReport ). Aggiungiamo un nuovo unit test al database:

Aggiungi il seguente codice di unit test:

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

Esegui anche il test unitario che deve fallire per essere conforme a TDDD:

Aggiungi la funzionalità richiesta all'oggetto ArticlesPerAuthorReport

Lo unit test che verifica la funzionalità dell'oggetto richiede una struttura modificata affinché il test possa passare.

Modifica il ArticoliPerAutoreReport vista per far passare il secondo unit test proprio come il primo:

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

L'oggetto database è stato modificato correttamente per produrre i dati desiderati. Esegui tutti gli unit test:

Il ArticoliPerAutoreReport l'oggetto è pronto.

Il nostro prossimo compito è fornire una procedura dettagliata per la creazione di una base di report su un oggetto di database sviluppato e testato utilizzando lo sviluppo basato su test (TDDD).

Implementazione del requisito di segnalazione (ArticlesPerAuthorReport)

Innanzitutto, ripristineremo SQLDevBlogReportTDD e aggiungi più dati ad esso. Oppure puoi creare un database vuoto per la prima volta.

Per aggiungere dati sufficienti al nostro database di esempio, recupera i dati da vwAuthors visualizza per vedere tutti i record:

Esecuzione degli unit test

Esegui gli unit test del database che abbiamo creato in precedenza:

Congratulazioni, entrambi i test sono stati superati, il che significa che l'oggetto database desiderato è in grado di soddisfare i requisiti di segnalazione per visualizzare gli articoli per autore.

Crea rapporto database basato sull'oggetto ArticlesPerAuthorsReport

È possibile creare un report di database in molti modi (ad esempio utilizzando Generatore report, creando un progetto server di report in Visual Studio Data Tools o utilizzando dbForge Studio per SQL Server).

In questa sezione, utilizzeremo dbForge Studio per SQL Server per la creazione di report. Per procedere, fai clic su Nuovo dal File Menu> Rapporto dati :

Fare clic su Rapporto standard :

Seleziona Tabella semplice\Visualizza come Tipo di dati :

Aggiungi l'oggetto di base (ArticoliPerAuthorReport ), che è una vista nel nostro caso:

Aggiungi i campi obbligatori:

Non abbiamo bisogno di alcun raggruppamento a questo punto, quindi procedi facendo clic su Avanti :

Seleziona Layout e Orientamento del rapporto dati:

Infine, aggiungi il titolo Rapporto articoli per autore e fai clic su Fine :

Quindi, regola la formattazione del rapporto secondo i requisiti:

Fai clic su Anteprima per vedere il rapporto del database:

Salva il rapporto come ArticoliPerAuthorReport . Il report del database è stato creato per esigenze aziendali.

Utilizzo della procedura di configurazione

Quando scrivi unit test di database usando tSQLt, vedrai che alcuni codici di test vengono ripetuti spesso. Pertanto, puoi definirlo in una procedura di installazione e riutilizzarlo in seguito in altri test unitari di quella particolare classe di test. Ciascuna classe di test può avere solo una procedura di configurazione che viene eseguita automaticamente prima dei processi di unit test di quella classe.

Possiamo inserire quasi tutto il codice di test scritto in Assembla (sezione) nella procedura di configurazione per evitare la duplicazione del codice.

Ad esempio, dobbiamo creare tabelle derise insieme a una tabella prevista. Quindi aggiungeremo i dati alle tabelle prese in giro e quindi alla tabella prevista. Possiamo definirlo facilmente nella procedura di configurazione e riutilizzarlo ulteriormente.

Creazione della procedura di configurazione per evitare la duplicazione del codice di test

Crea una procedura memorizzata in SQLDevBlogTDD come segue:

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

Ora rimuovi il codice di test che abbiamo scritto nella procedura di installazione dal precedente unit test per controllare ArticlesPerAuthorReport uscite come segue:

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

Nota :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.

Conclusione

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!