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

Direkt zum Start der testgetriebenen Datenbankentwicklung (TDDD)

In der Regel beginnen wir mit der Entwicklung von Datenbanklösungen, indem wir Datenbankobjekte wie Tabellen, Ansichten, gespeicherte Prozeduren usw. basierend auf den Geschäftsanforderungen erstellen. Dieser Ansatz wird auch als Konventionelle Datenbankentwicklung bezeichnet . In diesem Artikel werden wir diesen Ansatz untersuchen und mit Beispielen veranschaulichen.

Konventionelle Datenbankentwicklung

Der Entwicklungsstil besteht aus den folgenden Schritten:

  1. Anforderungen erhalten
  2. Datenbankobjekte basierend auf Anforderungen erstellen
  3. Führen Sie Komponententests für Datenbankobjekte durch, um zu sehen, ob sie die Anforderungen erfüllen
  4. Neue Anforderungen erhalten
  5. Ändern Sie vorhandene Datenbankobjekte oder fügen Sie neue hinzu, um die neuen Anforderungen zu erfüllen
  6. Erstellen und führen Sie Unit-Tests durch, um zu überprüfen, ob neue Anforderungen entsprechend funktionieren und nicht mit den vorherigen in Konflikt stehen

Um die Prozesse zu untersuchen und zu veranschaulichen, beginnen wir mit dem Einrichten einer Beispieldatenbank SQLDevBlog :

 
-- Create sample database (SQLDevBlog)
  CREATE DATABASE SQLDevBlog

Verwenden Sie den folgenden Code, um Tabellen in dieser Beispieldatenbank zu erstellen:

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

Überprüfen Sie das Datenbankdiagramm mit unseren neu erstellten Tabellen:

Hinweis :Ich verwende hier dbForge Studio für SQL Server, um alle Aufgaben zu erledigen. Das Aussehen der Ausgabe kann sich von SSMS (SQL Server Management Studio) unterscheiden, aber die Ergebnisse sind dieselben.

Als Nächstes füllen wir unser SQLDevBlog Beispieldatenbank, um ein realistischeres Szenario zu erstellen:

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

Als Ergebnis haben wir die folgenden gefüllten Tabellen:

Nachdem wir nun mit der Einrichtung der Datenbank und dem Auffüllen der Tabellen fertig sind, stehen wir vor dem nächsten Schritt. Wir müssen das Szenario mit einer neuen Anforderung nachahmen.

Die Anforderung, eine neue Kategorie hinzuzufügen

Eine neue Anforderung besagt, dass ein Administrator in der Lage sein sollte, eine neue Kategorie zur Liste der verfügbaren Kategorien hinzuzufügen . Um diese Anforderung zu erfüllen, muss Ihr Entwicklungsteam eine gespeicherte Prozedur zum einfachen Hinzufügen einer neuen Anforderung entwickeln. Oder wir müssen eine AddCategory erstellen Datenbankobjekt.

Führen Sie zum Erstellen der gespeicherten Prozedur das folgende Skript aus:

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

Das Ergebnis lautet wie folgt:

Erstellen Sie einen Datenbank-Einheitentest, um zu überprüfen, ob das Verfahren ordnungsgemäß funktioniert

Der nächste Schritt besteht darin, den Datenbank-Einheitentest zu erstellen, um zu überprüfen, ob die gespeicherte Prozedur die Spezifikation erfüllt.

Dieser Tipp funktioniert für dbForge Studio for SQL Server (oder nur dbForge-Einheitentest ) und SSMS (SQL Server Management Studio) . Hinweis:Stellen Sie bei Verwendung von SSMS (SQL Server Management Studio) sicher, dass Sie tSQLt installieren Framework zum Schreiben der Unit-Tests.

Um den ersten Datenbankkomponententest zu erstellen, klicken Sie mit der rechten Maustaste auf SQLDevBlog Datenbank> Einheitentest > Neuen Test hinzufügen

Der Neuen Test hinzufügen Fenster öffnet sich. Füllen Sie alle erforderlichen Informationen aus und klicken Sie auf Test hinzufügen .

Erstellen Sie den Komponententest wie folgt und speichern Sie ihn:

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

Klicken Sie auf die Datenbank Menü> Einheitentest > Testliste anzeigen und führen Sie den Komponententest wie unten gezeigt aus:

Wir können sehen, dass der Komponententest erfolgreich ist. Somit kann der Datenbank (Tabelle) eine neue Kategorie hinzugefügt werden. Die Anforderung wurde erfüllt.

Jetzt werden wir die testgetriebene Datenbankentwicklung untersuchen und beschreiben, wie der Prozess des Schreibens von Komponententests die Anforderungen erfüllen kann.

Testgetriebene Datenbankentwicklung (TDDD)

Die testgetriebene Datenbankentwicklung (TDDD) beginnt mit dem Schreiben des Komponententests, der zuerst fehlschlägt. Dann ändern wir es so, dass es besteht, und verfeinern es dann.

Komponententests werden geschrieben, um Anforderungen und Komponententests zu erfüllen, die erfordern, dass Datenbankobjekte erstellt und ordnungsgemäß ausgeführt werden.

Um den Unterschied zwischen traditioneller Datenbankentwicklung und testgetriebener Datenbankentwicklung zu verstehen, lassen Sie uns das SQLDevBlogTDD erstellen Datenbank. Es ist dasselbe wie SQLDevBlog .

-- Create sample database (SQLDevBlogTDD)
  CREATE DATABASE SQLDevBlogTDD

Füllen Sie dann die Beispieldatenbank mit Tabellen:

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

Wir müssen unsere Beispieldatenbank wie folgt füllen, um ein realistischeres Szenario zu erstellen:

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

Anforderung zum Hinzufügen einer neuen Kategorie (TDDD)

Jetzt haben wir die gleiche Anforderung:Der Administrator sollte in der Lage sein, eine neue Kategorie zur Liste der verfügbaren Kategorien hinzuzufügen. Um die Anforderung zu erfüllen, müssen wir zuerst einen Datenbankkomponententest schreiben, der nach einem potenziellen Objekt sucht.

So funktioniert TDDD:Der Unit-Test schlägt zunächst fehl, da wir davon ausgehen, dass wir nach dem Objekt suchen, das derzeit nicht existiert, aber bald dort sein wird.

Erstellen Sie den Datenbankeinheitentest und führen Sie ihn aus, um zu überprüfen, ob das gewünschte Objekt vorhanden ist

Obwohl wir wissen, dass es jetzt nicht existiert, betrachten wir dies als Ausgangspunkt.

In dbForge Studio für SQL Server wird der Datenbankkomponententest, der TDDD standardmäßig unterstützt, so erstellt, dass er zuerst fehlschlägt. Dann werden wir es ein wenig ändern. Wenn Sie ein tSQLt verwenden Datenbank-Unit-Test-Framework direkt schreiben Sie den folgenden 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

Nachdem Sie den Datenbankkomponententest ausgeführt haben, können Sie sehen, dass der Test fehlschlägt:

Erstellen Sie das Datenbankobjekt und führen Sie den Komponententest erneut aus

Der nächste Schritt besteht darin, das erforderliche Datenbankobjekt zu erstellen. In unserem Fall handelt es sich um eine gespeicherte Prozedur.

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

Führen Sie den Komponententest erneut aus – diesmal gelingt er:

Es reicht jedoch nicht aus, den Unit-Test zu bestehen, um zu prüfen, ob die gespeicherte Prozedur vorhanden ist. Wir müssen auch prüfen, ob die gespeicherte Prozedur eine neue Kategorie hinzufügt.

Erstellen Sie den Datenbank-Einheitentest, um zu überprüfen, ob die Routine ordnungsgemäß funktioniert

Lassen Sie uns den neuen Datenbank-Einheitentest erstellen:

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

Wie Sie sehen können, schlägt der Komponententest beim ersten Mal fehl und beim zweiten Mal erfolgreich:

Fügen Sie der Routine Funktionalität hinzu und führen Sie den Einheitentest erneut aus

Ändern Sie die gespeicherte Prozedur, indem Sie die erforderliche Funktionalität hinzufügen, damit der Test wie unten gezeigt erfolgreich sein kann:

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

Führen Sie die Komponententests erneut aus, um zu überprüfen, ob sie alle erfolgreich sind, einschließlich der kürzlich geänderten gespeicherten Prozedur:

Auf diese Weise haben wir die testgetriebene Datenbankentwicklung erfolgreich implementiert. Jetzt können wir uns nur noch auf die Anforderungen konzentrieren. Die Komponententests kapseln Anforderungen und fordern damit, dass Datenbankobjekte erstellt und ordnungsgemäß ausgeführt werden, um die Spezifikation zu erfüllen.

Mal sehen, wie effektiv TDDD ist, wenn es darum geht, eine Geschäftsberichtspflicht zu erfüllen.

Erfüllung der Anforderungen an die Geschäftsberichterstattung durch TDDD

Wir gehen davon aus, dass die Datenbank bereits die erforderlichen Objekte (z. B. Tabellen) erhalten hat, bevor sie die neuen Anforderungen an die Geschäftsberichterstattung erhält.

Lassen Sie uns eine Beispieldatenbank namens SQLDevBlogReportTDD erstellen :

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

Erstellen Sie dann die Tabellen für die Beispieldatenbank und füllen Sie sie mit folgendem Code:

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

Erstellen Sie eine Ansicht, um die Liste aller Autoren, Artikel und Artikelkategorien anzuzeigen:

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

Führen Sie die Ansicht der erstellten Tabelle aus, um die Ergebnisse anzuzeigen:

Nach der Bearbeitung der Datenbankeinrichtung und dem Füllen der Tabellen besteht der nächste Schritt darin, das Szenario zu imitieren, in dem wir eine neue Anforderung erhalten haben.

Geschäftsanforderung:Gesamtzahl der Artikel pro Autorenbericht

Erwägen Sie eine neue Geschäftsberichtspflicht. Es muss ein Datenbankbericht angegeben werden, um die Gesamtzahl der Artikel pro Autor anzuzeigen.

Zunächst muss ein Datenbankobjekt zugewiesen werden, das den geschäftlichen Anforderungen entspricht. In unserem Fall ist es der ArticlesPerAuthorReport Datenbankobjekt.

Um die Anforderung zu erfüllen, muss ein Datenbankkomponententest erstellt werden, der nach einem potenziell geeigneten Objekt sucht. Wie wir wissen, schlägt dieser Test zuerst fehl, weil er nach dem Objekt sucht, das im Moment nicht existiert, aber bald da sein wird.

TDDD-Implementierungsplan

Gemäß den Standards für testgetriebene Datenbankkomponententests müssen die folgenden Dinge vorhanden sein, um die Meldepflicht zu erfüllen:

  1. Entwickeln Sie ein einzelnes Datenbankobjekt, das die Berichtsanforderungen erfüllt.
  2. Erstellen Sie einen Einheitentest, um die Existenz des Objekts zu überprüfen.
  3. Erstellen Sie einen Objekt-Stub (Platzhalter), um den ersten Test zu bestehen.
  4. Erstellen Sie einen zweiten Komponententest, um zu überprüfen, ob das Objekt korrekte Daten in die Tabelle mit korrekter Eingabe ausgibt.
  5. Ändern Sie die Objektdefinition, damit der zweite Test bestanden wird.

Erstellen Sie den Datenbankkomponententest, um zu überprüfen, ob das gewünschte Objekt vorhanden ist

Wir weisen ein Datenbankobjekt zu, das die Geschäftsanforderungen erfüllen kann. In unserem Fall ist es der ArticlesPerAuthorReport Datenbankobjekt. Der nächste Schritt besteht darin, einen Datenbankkomponententest zu erstellen.

Um den ersten Datenbankkomponententest zu erstellen, klicken Sie mit der rechten Maustaste auf SQLDevBlogReport Datenbank> Einheitentest > Neuen Test hinzufügen

Schreiben Sie den folgenden Code, um den Komponententest zu erstellen, der das Vorhandensein oder Fehlen des gewünschten Objekts überprüft:

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

Der Komponententest muss fehlschlagen, da er nach dem zuvor erstellten Objekt sucht. Das Objekt selbst wird erstellt, um TDDD zu entsprechen:

Objekt-Stub erstellen und Einheit ausführen

Erstellen Sie einen Objekt-Stub mit einer hartcodierten erwarteten Ausgabe, da wir nur ein Datenbankobjekt mit dem erwarteten Ergebnis erstellen möchten. Erstellen Sie den ArticlesPerAuthorReport Objekt zunächst als View-Stub (Platzhalter):

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

Das Ausführen des Komponententests sollte erfolgreich sein:

Das Erstellen eines Stubs dient als Kickstarter für TDDD. Wir erstellen das Objekt, um den Test zu bestehen und kümmern uns nicht um die eigentliche Funktionsweise des Objekts.

Erstellen Sie den Einheitentest und führen Sie ihn aus, um zu prüfen, ob das Objekt korrekte Daten ausgibt

Es ist Zeit zu überprüfen, ob das gewünschte Objekt ordnungsgemäß funktioniert. Erstellen Sie einen weiteren Komponententest, um die Datenausgabe des gewünschten Objekts zu überprüfen (ArticlesPerAuthorReport ). Fügen wir der Datenbank einen neuen Komponententest hinzu:

Fügen Sie den folgenden Komponententestcode hinzu:

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

Führen Sie den Komponententest aus, der ebenfalls fehlschlagen muss, um TDDD zu erfüllen:

Erforderliche Funktionalität zum ArticlesPerAuthorReport-Objekt hinzufügen

Der Unit-Test, der die Funktionalität des Objekts überprüft, benötigt eine modifizierte Struktur, damit der Test bestehen kann.

Ändern Sie den ArticlesPerAuthorReport view, um den zweiten Unit-Test genauso wie den ersten durchlaufen zu lassen:

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

Das Datenbankobjekt wurde erfolgreich geändert, um die gewünschten Daten auszugeben. Führen Sie alle Unit-Tests aus:

Der ArticlesPerAuthorReport Objekt ist fertig.

Unsere nächste Aufgabe besteht darin, eine exemplarische Vorgehensweise zum Erstellen einer Berichtsbasis auf einem Datenbankobjekt bereitzustellen, das mithilfe von testgetriebener Entwicklung (TDDD) entwickelt und getestet wurde.

Umsetzung der Meldepflicht (ArticlesPerAuthorReport)

Zuerst werden wir SQLDevBlogReportTDD zurücksetzen und weitere Daten hinzufügen. Oder Sie können zum ersten Mal eine leere Datenbank erstellen.

Um genügend Daten zu unserer Beispieldatenbank hinzuzufügen, rufen Sie Daten von vwAuthors ab view um alle Datensätze zu sehen:

Einheitentests ausführen

Führen Sie die zuvor erstellten Datenbank-Einheitentests aus:

Herzlichen Glückwunsch, beide Tests wurden bestanden, was bedeutet, dass das gewünschte Datenbankobjekt in der Lage ist, die Berichtsanforderungen zum Anzeigen von Artikeln pro Autor zu erfüllen.

Datenbankbericht basierend auf ArticlesPerAuthorsReport-Objekt erstellen

Sie können einen Datenbankbericht auf viele Arten erstellen (z. B. mit Report Builder, Erstellen eines Berichtsserverprojekts in Visual Studio Data Tools oder mit dbForge Studio für SQL Server).

In diesem Abschnitt verwenden wir dbForge Studio für SQL Server zum Erstellen von Berichten. Klicken Sie zum Fortfahren auf Neu aus der Datei Menü> Datenbericht :

Klicken Sie auf Standardbericht :

Wählen Sie Einfache Tabelle\Ansicht aus als Datentyp :

Fügen Sie das Basisobjekt hinzu (ArticlesPerAuthorReport ), was in unserem Fall eine Ansicht ist:

Fügen Sie die erforderlichen Felder hinzu:

Wir brauchen an dieser Stelle keine Gruppierung, also fahren Sie fort, indem Sie auf Weiter klicken :

Wählen Sie Layout aus und Orientierung des Datenberichts:

Fügen Sie schließlich den Titel Artikel pro Autorenbericht hinzu und klicken Sie auf Fertig stellen :

Passen Sie als Nächstes die Formatierung des Berichts gemäß den Anforderungen an:

Klicken Sie auf Vorschau um den Datenbankbericht zu sehen:

Speichern Sie den Bericht als ArticlesPerAuthorReport . Der Datenbankbericht wurde aufgrund der geschäftlichen Anforderungen erstellt.

Verwendung des Setup-Verfahrens

Wenn Sie Komponententests für Datenbanken mit tSQLt schreiben, werden Sie feststellen, dass ein Teil des Testcodes häufig wiederholt wird. Daher können Sie es in einer Setup-Prozedur definieren und anschließend in anderen Komponententests dieser bestimmten Testklasse wiederverwenden. Jede Testklasse kann nur eine Einrichtungsprozedur haben, die automatisch ausgeführt wird, bevor die Komponententests dieser Klasse verarbeitet werden.

Wir können fast jeden geschriebenen Testcode unter Assemble stellen (Abschnitt) im Setup-Verfahren, um Codeduplizierung zu vermeiden.

Zum Beispiel müssen wir Mock-Tabellen zusammen mit einer erwarteten Tabelle erstellen. Dann fügen wir Daten zu simulierten Tabellen und dann zur erwarteten Tabelle hinzu. Wir können es einfach unter der Einrichtungsprozedur definieren und weiterverwenden.

Erstellen einer Setup-Prozedur zur Vermeidung von Testcode-Duplizierung

Erstellen Sie eine gespeicherte Prozedur in SQLDevBlogTDD wie folgt:

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

Entfernen Sie nun den Testcode, den wir in der Einrichtungsprozedur aus dem vorherigen Komponententest geschrieben haben, um ArticlesPerAuthorReport zu überprüfen gibt wie folgt aus:

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

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

Schlussfolgerung

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!