Database
 sql >> Base de données >  >> RDS >> Database

Aller au début du développement de base de données piloté par les tests (TDDD)

En règle générale, nous commençons à développer des solutions de base de données en créant des objets de base de données, tels que des tables, des vues, des procédures stockées, etc., en fonction des besoins de l'entreprise. Cette approche est également connue sous le nom de Développement de base de données conventionnelle . Dans cet article, nous allons explorer cette approche et l'illustrer par des exemples.

Développement de base de données conventionnelle

Le style de développement comprend les étapes suivantes :

  1. Recevoir les exigences
  2. Créer des objets de base de données en fonction des besoins
  3. Exécuter des tests unitaires pour les objets de base de données pour voir s'ils répondent aux exigences
  4. Recevoir les nouvelles exigences
  5. Modifier les objets de base de données existants ou en ajouter de nouveaux pour répondre aux nouvelles exigences
  6. Créer et exécuter des tests unitaires pour vérifier si les nouvelles exigences fonctionnent en conséquence et n'entrent pas en conflit avec les précédentes

Pour explorer et illustrer les processus, commençons par configurer un exemple de base de données SQLDevBlog :

 
-- Create sample database (SQLDevBlog)
  CREATE DATABASE SQLDevBlog

Utilisez le code suivant pour créer des tables dans cet exemple de base de données :

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

Passez en revue le diagramme de la base de données avec nos tables nouvellement créées :

Remarque :J'utilise ici dbForge Studio pour SQL Server pour effectuer toutes les tâches. L'aspect de sa sortie peut différer de SSMS (SQL Server Management Studio), mais les résultats sont les mêmes.

Ensuite, nous remplirons notre SQLDevBlog exemple de base de données pour créer un scénario plus réaliste :

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

En conséquence, nous avons les tables peuplées suivantes :

Maintenant que nous en avons terminé avec la configuration de la base de données et le remplissage des tables, nous passons à l'étape suivante. Nous devons imiter le scénario avec une nouvelle exigence.

L'exigence d'ajouter une nouvelle catégorie

Une nouvelle exigence stipule qu'un administrateur doit pouvoir ajouter une nouvelle catégorie à la liste des catégories disponibles . Pour répondre à cette exigence, votre équipe de développement doit proposer une procédure stockée pour ajouter facilement une nouvelle exigence. Ou, nous devons créer un AddCategory Objet de base de données.

Pour créer la procédure stockée, exécutez le script suivant :

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

Le résultat est le suivant :

Créer un test unitaire de base de données pour vérifier si la procédure fonctionne correctement

L'étape suivante consiste à créer le test unitaire de la base de données pour vérifier si la procédure stockée répond à la spécification.

Cette astuce fonctionne pour dbForge Studio for SQL Server (ou uniquement Test unitaire dbForge ) et SSMS (SQL Server Management Studio) . Remarque :Lorsque vous utilisez SSMS (SQL Server Management Studio), assurez-vous d'installer le tSQLt Framework pour écrire les tests unitaires.

Pour créer le premier test unitaire de base de données, cliquez avec le bouton droit sur SQLDevBlog base de données> Test unitaire > Ajouter un nouveau test

Le bouton Ajouter un nouveau test fenêtre s'ouvre. Remplissez toutes les informations requises et cliquez sur Ajouter un test .

Créez le test unitaire comme suit et enregistrez-le :

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

Cliquez sur la base de données menu> Test unitaire > Afficher la liste des tests et exécutez le test unitaire comme indiqué ci-dessous :

Nous pouvons voir que le test unitaire est réussi. Ainsi, une nouvelle catégorie peut être ajoutée à la base de données (table). L'exigence a été satisfaite.

Nous allons maintenant explorer le développement de bases de données piloté par les tests et décrire comment le processus d'écriture de tests unitaires peut répondre aux exigences.

Développement de base de données piloté par les tests (TDDD)

Le développement de base de données piloté par les tests (TDDD) commence par l'écriture du test unitaire qui échouera en premier. Ensuite, nous le modifierons pour qu'il passe, puis nous l'affinerons.

Les tests unitaires sont écrits pour répondre aux exigences et aux tests unitaires qui nécessitent la création et l'exécution correctes d'objets de base de données.

Pour comprendre la différence entre le développement de base de données traditionnel et le développement de base de données piloté par les tests, créons le SQLDevBlogTDD base de données. C'est la même chose que SQLDevBlog .

-- Create sample database (SQLDevBlogTDD)
  CREATE DATABASE SQLDevBlogTDD

Ensuite, remplissez l'exemple de base de données avec des tables :

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

Nous devons remplir notre exemple de base de données pour créer un scénario plus réaliste comme suit :

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

Exigence d'ajouter une nouvelle catégorie (TDDD)

Maintenant, nous avons la même exigence :l'administrateur doit pouvoir ajouter une nouvelle catégorie à la liste des catégories disponibles. Pour répondre à l'exigence, nous devons d'abord écrire un test unitaire de base de données qui recherche un objet potentiel.

C'est ainsi que fonctionne TDDD :le test unitaire échoue d'abord car nous supposons que nous recherchons l'objet qui n'existe pas actuellement, mais qui sera bientôt là.

Créer et exécuter le test unitaire de la base de données pour vérifier l'existence de l'objet souhaité

Bien que nous sachions qu'il n'existe pas maintenant, nous considérons cela comme un point de départ.

Dans dbForge Studio pour SQL Server, le test unitaire de base de données qui prend en charge TDDD par défaut est créé pour échouer en premier. Ensuite, nous allons le modifier un peu. Si vous utilisez un tSQLt cadre de test unitaire de base de données directement, écrivez le test unitaire suivant :

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

Après avoir exécuté le test unitaire de la base de données, vous pouvez voir que le test échoue :

Créer l'objet de base de données et relancer le test unitaire

L'étape suivante consiste à créer l'objet de base de données requis. Dans notre cas, il s'agit d'une procédure stockée.

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

Exécutez à nouveau le test unitaire - cette fois, il réussit :

Mais il ne suffit pas de réussir le test unitaire en vérifiant si la procédure stockée existe. Nous devons également vérifier si la procédure stockée ajoute une nouvelle catégorie.

Créer le test unitaire de la base de données pour vérifier si la routine fonctionne correctement

Créons le nouveau test unitaire de base de données :

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

Comme vous pouvez le voir, le test unitaire échoue une première fois et réussit une deuxième fois :

Ajouter des fonctionnalités au test unitaire de routine et de réexécution

Modifiez la procédure stockée en ajoutant la fonctionnalité requise afin que le test puisse réussir comme indiqué ci-dessous :

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

Réexécutez les tests unitaires pour vérifier qu'ils réussissent tous, y compris la procédure stockée récemment modifiée :

De cette façon, nous avons réussi à mettre en œuvre le développement de bases de données piloté par les tests. Maintenant, nous pouvons nous concentrer uniquement sur les exigences. Les tests unitaires encapsulent les exigences, exigeant ainsi que les objets de base de données soient créés et exécutés correctement pour répondre à la spécification.

Voyons à quel point TDDD est efficace lorsqu'il s'agit de satisfaire à une exigence de déclaration d'entreprise.

Satisfaire aux exigences en matière de rapports commerciaux via TDDD

Nous supposons que la base de données a déjà obtenu les objets nécessaires (tels que des tables) avant de recevoir la nouvelle exigence de création de rapports commerciaux.

Créons un exemple de base de données appelée SQLDevBlogReportTDD :

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

Ensuite, créez et remplissez les tables de l'exemple de base de données à l'aide du code suivant :

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

Créez une vue pour voir la liste de tous les auteurs, articles et catégories d'articles :

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

Exécutez la vue du tableau créé pour voir les résultats :

Après avoir traité la configuration de la base de données et rempli les tables, l'étape suivante consiste à imiter le scénario dans lequel nous avons reçu une nouvelle exigence.

Exigence commerciale :nombre total d'articles par rapport d'auteur

Envisager une nouvelle exigence de déclaration des entreprises. Il doit indiquer un rapport de base de données pour afficher le nombre total d'articles par auteur.

La première chose à faire est d'attribuer un objet de base de données qui peut répondre aux besoins de l'entreprise. Dans notre cas, il s'agit du ArticlesPerAuthorReport objet de base de données.

Pour répondre à l'exigence, il est nécessaire de créer un test unitaire de base de données qui recherche un objet potentiel approprié. Comme nous le savons, ce test échouera en premier car il recherchera l'objet qui n'existe pas pour le moment mais qui sera bientôt là.

Plan de mise en œuvre du TDDD

Selon les normes des tests unitaires de base de données pilotés par les tests, les éléments suivants doivent être présents pour répondre à l'exigence de création de rapports :

  1. Développez un objet de base de données unique qui répondra aux exigences de création de rapports.
  2. Créez un test unitaire pour vérifier l'existence de l'objet.
  3. Créez un stub d'objet (espace réservé) pour réussir le premier test.
  4. Créez un deuxième test unitaire pour vérifier si l'objet génère des données correctes dans la table avec une entrée correcte.
  5. Modifiez la définition de l'objet pour laisser passer le deuxième test.

Créer le test unitaire de la base de données pour vérifier l'existence de l'objet souhaité

Nous attribuerons un objet de base de données qui peut répondre aux besoins de l'entreprise. Dans notre cas, il s'agit du ArticlesPerAuthorReport objet de base de données. L'étape suivante consiste à créer un test unitaire de base de données.

Pour créer le premier test unitaire de base de données, cliquez avec le bouton droit sur SQLDevBlogReport base de données> Test unitaire > Ajouter un nouveau test

Écrivez le code suivant pour créer le test unitaire qui vérifie l'existence ou l'absence de l'objet souhaité :

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

Le test unitaire doit échouer car il vérifie l'objet créé auparavant. L'objet lui-même est créé pour se conformer à TDDD :

Créer un stub d'objet et exécuter l'unité

Créez un stub d'objet avec une sortie attendue codée en dur, car nous voulons simplement créer un objet de base de données avec le résultat attendu. Créez le ArticlesPerAuthorReport objet en tant que stub de vue (espace réservé) dans un premier temps :

-- (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'exécution du test unitaire devrait réussir :

La création d'un stub sert de tremplin pour TDDD. Nous créons l'objet pour réussir le test et ne nous soucions pas du fonctionnement réel de l'objet.

Créer et exécuter le test unitaire pour vérifier si l'objet produit des données correctes

Il est temps de vérifier si l'objet souhaité fonctionne correctement. Créez un autre test unitaire pour vérifier la sortie de données par l'objet souhaité (ArticlesPerAuthorReport ). Ajoutons un nouveau test unitaire à la base de données :

Ajoutez le code de test unitaire suivant :

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

Exécutez le test unitaire qui doit également échouer pour se conformer à TDDD :

Ajouter la fonctionnalité requise à l'objet ArticlesPerAuthorReport

Le test unitaire qui vérifie la fonctionnalité de l'objet nécessite une structure modifiée pour que le test puisse réussir.

Modifier le ArticlesPerAuthorReport view pour laisser passer le second test unitaire comme le premier :

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'objet de base de données a été modifié avec succès pour générer les données souhaitées. Exécutez tous les tests unitaires :

Le ArticlesPerAuthorReport l'objet est prêt.

Notre tâche suivante consiste à fournir une présentation de la création d'une base de rapport sur un objet de base de données développé et testé à l'aide du développement piloté par les tests (TDDD).

Mise en œuvre de l'exigence de rapport (ArticlesPerAuthorReport)

Tout d'abord, nous allons réinitialiser le SQLDevBlogReportTDD et ajoutez-y plus de données. Ou, vous pouvez créer une base de données vierge pour la première fois.

Pour ajouter suffisamment de données à notre exemple de base de données, récupérez les données de vwAuthors view pour voir tous les enregistrements :

Exécuter les tests unitaires

Exécutez les tests unitaires de base de données que nous avons créés précédemment :

Félicitations, les deux tests ont réussi, ce qui signifie que l'objet de base de données souhaité est capable de répondre aux exigences de création de rapports pour afficher les articles par auteur.

Créer un rapport de base de données basé sur l'objet ArticlesPerAuthorsReport

Vous pouvez créer un rapport de base de données de plusieurs manières (par exemple, en utilisant le Générateur de rapports, en créant un projet de serveur de rapports dans Visual Studio Data Tools ou en utilisant dbForge Studio pour SQL Server).

Dans cette section, nous utilisons dbForge Studio pour SQL Server pour la création de rapports. Pour continuer, cliquez sur Nouveau à partir du Fichier Menu > Rapport de données :

Cliquez sur Rapport standard :

Sélectionnez Tableau simple\Afficher comme Type de données :

Ajoutez l'objet de base (ArticlesPerAuthorReport ), qui est une vue dans notre cas :

Ajoutez les champs obligatoires :

Nous n'avons pas besoin de regroupement à ce stade, alors continuez en cliquant sur Suivant :

Sélectionnez Mise en page et Orientation du rapport de données :

Enfin, ajoutez le titre Articles par rapport d'auteur et cliquez sur Terminer :

Ensuite, ajustez la mise en forme du rapport selon vos besoins :

Cliquez sur Aperçu pour voir le rapport de la base de données :

Enregistrez le rapport sous ArticlesPerAuthorReport . Le rapport de base de données a été créé en raison des besoins de l'entreprise.

Utilisation de la procédure de configuration

Lorsque vous écrivez des tests unitaires de base de données à l'aide de tSQLt, vous verrez que certains codes de test sont souvent répétés. Ainsi, vous pouvez le définir dans une procédure de configuration et le réutiliser ensuite dans d'autres tests unitaires de cette classe de test particulière. Chaque classe de test ne peut avoir qu'une seule procédure de configuration qui s'exécute automatiquement avant les tests unitaires de cette classe de processus.

Nous pouvons mettre presque tout le code de test écrit sous Assembler (section) sous la procédure de configuration pour éviter la duplication de code.

Par exemple, nous devons créer des tables simulées avec une table attendue. Ensuite, nous ajouterons des données aux tables fictives, puis à la table attendue. Nous pouvons le définir facilement dans la procédure de configuration et le réutiliser ultérieurement.

Création d'une procédure de configuration pour éviter la duplication du code de test

Créer une procédure stockée dans SQLDevBlogTDD comme suit :

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

Maintenant, supprimez le code de test que nous avons écrit dans la procédure de configuration du test unitaire précédent pour vérifier ArticlesPerAuthorReport sorties comme suit :

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

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

Conclusion

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!