Database
 sql >> база данни >  >> RDS >> Database

Преминете към стартиране на разработване на база данни, управлявано от тестове (TDDD)

Като правило започваме да разработваме решения за бази данни, като създаваме обекти на база данни, като таблици, изгледи, съхранени процедури и т.н., базирани на бизнес изисквания. Този подход е известен още като Конвенционална разработка на бази данни . В тази статия ще проучим този подход и ще го илюстрираме с примери.

Конвенционална разработка на бази данни

Стилът на разработка се състои от следните стъпки:

  1. Получете изискванията
  2. Създавайте обекти на база данни въз основа на изисквания
  3. Изпълнете модульни тестове за обекти на база данни, за да видите дали отговарят на изискванията
  4. Получаване на нови изисквания
  5. Променете съществуващи обекти на база данни или добавете нови, за да отговарят на новите изисквания
  6. Създайте и стартирайте модулни тестове, за да проверите дали новите изисквания работят съответно и не са в конфликт с предишните

За да изследваме и илюстрираме процесите, нека започнем със създаване на примерна база данни SQLDevBlog :

 
-- Create sample database (SQLDevBlog)
  CREATE DATABASE SQLDevBlog

Използвайте следния код, за да създадете таблици в тази примерна база данни:

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

Прегледайте диаграмата на базата данни с нашите новосъздадени таблици:

Забележка :Използвам тук dbForge Studio за SQL Server за изпълнение на всички задачи. Видът на изхода му може да се различава от SSMS (SQL Server Management Studio), но резултатите са същите.

След това ще попълним нашия SQLDevBlog примерна база данни, за да създадете по-реалистичен сценарий:

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

В резултат на това имаме следните попълнени таблици:

След като приключихме с настройката на базата данни и популацията на таблици, сме изправени пред следващата стъпка. Трябва да имитираме сценария с ново изискване.

Изискването за добавяне на нова категория

Ново изискване гласи, че администраторът трябва да може да добави нова категория към списъка с налични категории . За да изпълни това изискване, вашият екип за разработка трябва да измисли съхранена процедура за добавяне на ново изискване с лекота. Или трябва да създадем AddCategory Обект на база данни.

За да създадете съхранената процедура, изпълнете следния скрипт:

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

Резултатът е следният:

Създайте тест за единица база данни, за да проверите дали процедурата работи правилно

Следващата стъпка е да създадете единичния тест на базата данни, за да проверите дали съхранената процедура отговаря на спецификацията.

Този съвет работи за dbForge Studio за SQL Server (или само dbForge Unit Test ) и SSMS (SQL Server Management Studio) . Забележка:Когато използвате SSMS (SQL Server Management Studio), не забравяйте да инсталирате tSQLt Рамка за писане на модулните тестове.

За да създадете първия тест за единица база данни, щракнете с десния бутон върху SQLDevBlog база данни> Unit Test > Добавяне на нов тест

Добавяне на нов тест прозорец се отваря. Попълнете цялата необходима информация и щракнете върху Добавяне на тест .

Създайте единичния тест, както следва и го запазете:

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

Щракнете върху База данни меню> Unit Test > Преглед на списъка с тестове и стартирайте единичния тест, както е показано по-долу:

Виждаме, че единичният тест е успешен. По този начин може да се добави нова категория към базата данни (таблица). Изискването е изпълнено.

Сега ще проучим разработването на база данни, управлявано от тестове, и ще опишем как процесът на писане на единични тестове може да удовлетвори изискванията.

Управлявана от тест разработка на база данни (TDDD)

Разработването на база данни, управлявано от тестове (TDDD) започва с писане на единичния тест, който ще се провали първи. След това ще го модифицираме, за да премине, и след това ще го прецизираме.

Единичните тестове са написани, за да отговарят на изискванията, и модулните тестове, които изискват обектите на базата данни да бъдат създадени и изпълнявани правилно.

За да разберем разликата между традиционното разработване на бази данни и разработването на база данни, управлявано от тестове, нека създадем SQLDevBlogTDD база данни. Това е същото като SQLDevBlog .

-- Create sample database (SQLDevBlogTDD)
  CREATE DATABASE SQLDevBlogTDD

След това попълнете примерната база данни с таблици:

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

Трябва да попълним нашата примерна база данни, за да създадем по-реалистичен сценарий, както следва:

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

Изискване за добавяне на нова категория (TDDD)

Сега имаме същото изискване:администраторът трябва да може да добави нова категория към списъка с налични категории. За да изпълним изискването, първо трябва да напишем единичен тест на базата данни, който търси потенциален обект.

Ето как работи TDDD:единичният тест първо се проваля, тъй като предполагаме, че търсим обекта, който в момента не съществува, но скоро ще бъде там.

Създайте и стартирайте теста на базата данни, за да проверите дали желаният обект съществува

Въпреки че знаем, че сега не съществува, мислим за това като за отправна точка.

В dbForge Studio за SQL Server единичният тест на базата данни, който поддържа TDDD по подразбиране, се създава, за да се провали първи. След това ще го променим малко. Ако използвате tSQLt рамка за единичен тест на базата данни директно, напишете следния единичен тест:

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

След като стартирате теста за единица база данни, можете да видите, че тестът е неуспешен:

Създайте обекта на базата данни и повторете единичния тест

Следващата стъпка е да създадете необходимия обект на база данни. В нашия случай това е съхранена процедура.

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

Стартирайте отново единичния тест – този път той е успешен:

Но не е достатъчно да преминете единичния тест, проверяващ дали съхранената процедура съществува. Трябва също да проверим дали съхранената процедура добавя нова категория.

Създайте тест за единица на базата данни, за да проверите дали рутинните функции функционират правилно

Нека създадем новия тест за единица база данни:

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

Както можете да видите, единичният тест се проваля за първи път и е успешен за втори път:

Добавяне на функционалност към рутинния и повторен модулен тест

Променете съхранената процедура, като добавите необходимата функционалност, за да може тестът да успее, както е показано по-долу:

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

Изпълнете отново модулните тестове, за да проверите дали всички те са успешни, включително наскоро модифицираната съхранена процедура:

По този начин успешно внедрихме разработка на база данни, управлявана от тестове. Сега можем да се съсредоточим само върху изискванията. Единичните тестове капсулират изискванията, като по този начин изискват обектите на базата данни да бъдат създадени и да работят правилно, за да отговарят на спецификацията.

Нека видим колко ефективен е TDDD, когато става въпрос за удовлетворяване на изискване за бизнес отчетност.

Удовлетворяване на изискванията за бизнес отчетност чрез TDDD

Предполагаме, че базата данни вече има необходимите обекти (като таблици), преди да получи новото изискване за бизнес отчет.

Нека създадем примерна база данни, наречена SQLDevBlogReportTDD :

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

След това създайте и попълнете таблиците за примерната база данни, като използвате следния код:

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

Създайте изглед, за да видите списъка с всички автори, статии и категории статии:

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

Стартирайте изгледа на създадената таблица, за да видите резултатите:

След обработката на настройката на базата данни и попълването на таблици, следващата стъпка е да имитираме сценария, при който получихме ново изискване.

Бизнес изискване:общ брой статии на доклад на автор

Помислете за ново изискване за бизнес отчетност. Той трябва да посочи отчет за база данни, за да види общия брой статии на автор.

Първото нещо е да зададете обект на база данни, който може да отговори на бизнес изискванията. В нашия случай това е ArticlesPerAuthorReport обект на база данни.

За да се отговори на изискването, е необходимо да се създаде един тест за база данни, който търси потенциално подходящ обект. Както знаем, този тест първо ще се провали, защото ще търси обекта, който не съществува в момента, но скоро ще бъде там.

План за изпълнение на TDDD

Според стандартите за тестване на база данни, управлявано от тестове, следните неща трябва да са налице, за да се изпълни изискването за докладване:

  1. Разработете един обект на база данни, който ще отговаря на изискването за отчитане.
  2. Създайте единичен тест, за да проверите съществуването на обекта.
  3. Създайте обект (заместител), за да преминете първия тест.
  4. Създайте втори единичен тест, за да проверите дали обектът извежда правилни данни в таблицата с правилно въвеждане.
  5. Променете дефиницията на обекта, за да позволите на втория тест да премине.

Създайте тест за единица на базата данни, за да проверите съществуването на желания обект

Ще присвоим обект на база данни, който може да отговори на бизнес изискването. В нашия случай това е ArticlesPerAuthorReport обект на база данни. Следващата стъпка е да създадете единичен тест за база данни.

За да създадете първия тест за единица база данни, щракнете с десния бутон върху SQLDevBlogReport база данни> Unit Test > Добавяне на нов тест

Напишете следния код, за да създадете единичния тест, който проверява съществуването или отсъствието на желания обект:

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

Единичният тест трябва да се провали, тъй като проверява за обекта, създаден преди това. Самият обект е създаден, за да отговаря на TDDD:

Създайте Object Stub и стартирайте модула

Създайте обектна заглушка с някакъв твърдо кодиран очакван изход, тъй като просто искаме да създадем обект на база данни с очаквания резултат. Създайте ArticlesPerAuthorReport обект отначало като изглед (заместител):

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

Изпълнението на единичния тест трябва да е успешно:

Създаването на мъниче служи като старт за TDDD. Създаваме обекта, за да премине теста и не се притесняваме за действителното функциониране на обекта.

Създайте и стартирайте Unit Test, за да проверите дали обектът извежда правилни данни

Време е да проверите дали желаният обект функционира правилно. Създайте друг единичен тест, за да проверите за извежданите данни от желания обект (ArticlesPerAuthorReport ). Нека добавим нов единичен тест към базата данни:

Добавете следния код за единичен тест:

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

Изпълнете единичния тест, който също трябва да се провали, за да отговаря на TDDD:

Добавете необходимата функционалност към обекта ArticlesPerAuthorReport

Единичният тест, който проверява функционалността на обекта, изисква модифицирана структура, така че тестът да може да премине.

Променете ArticlesPerAuthorReport преглед, за да позволите на втория единичен тест да премине точно като първия:

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

Обектът на базата данни е успешно променен за извеждане на желаните данни. Изпълнете всички модулни тестове:

Докладът ArticlesPerAuthorReport обектът е готов.

Следващата ни задача е да предоставим преглед на създаването на база за отчети върху обект на база данни, разработен и тестван с помощта на разработка, управлявана от тестове (TDDD).

Прилагане на изискване за отчитане (ArticlesPerAuthorReport)

Първо, ще нулираме SQLDevBlogReportTDD и добавете още данни към него. Или можете да създадете празна база данни за първи път.

За да добавите достатъчно данни към нашата примерна база данни, извлечете данни от vwAuthors преглед, за да видите всички записи:

Извършване на модулните тестове

Изпълнете единичните тестове на базата данни, които създадохме по-рано:

Поздравления, и двата теста преминаха, което означава, че желаният обект на база данни е в състояние да изпълни изискването за отчитане за преглед на статии на автор.

Създаване на отчет за база данни въз основа на обект ArticlesPerAuthorsReport

Можете да създадете отчет за база данни по много начини (като като използвате Report Builder, създаване на проект сървър на отчети в Visual Studio Data Tools или използване на dbForge Studio за SQL Server).

В този раздел използваме dbForge Studio за SQL Server за създаване на отчет. За да продължите, щракнете върху Ново от Файла Меню> Отчет за данни :

Кликнете върху Стандартен отчет :

Изберете Проста таблица\Изглед като Тип данни :

Добавете основния обект (ArticlesPerAuthorReport ), което е изглед в нашия случай:

Добавете задължителните полета:

В този момент нямаме нужда от групиране, така че продължете, като щракнете върху Напред :

Изберете Оформление иОриентация от отчета с данни:

Накрая добавете заглавие Статии за доклад на автор и щракнете върху Край :

След това коригирайте форматирането на отчета според изискването:

Щракнете върху Визуализация за да видите отчета за базата данни:

Запазете отчета като ArticlesPerAuthorReport . Отчетът за базата данни е създаден поради бизнес изискванията.

Използване на процедурата за настройка

Когато пишете единични тестове на база данни с помощта на tSQLt, ще видите, че някои тестови кодове се повтарят често. По този начин можете да го дефинирате в процедура за настройка и да го използвате повторно след това в други модулни тестове от този конкретен тестов клас. Всеки тестов клас може да има само една процедура за настройка, която се изпълнява автоматично преди модулните тестове на процесите на този клас.

Можем да поставим почти целия тестов код, написан под Ассемблиране (раздел) съгласно процедурата за настройка, за да избегнете дублиране на код.

Например, трябва да създадем подигравани таблици заедно с очаквана таблица. След това ще добавим данни към подиграваните таблици и след това към очакваната таблица. Можем да го дефинираме лесно при процедурата за настройка и да го използваме отново.

Създаване на процедура за настройка за избягване на дублиране на тестов код

Създайте съхранена процедура в SQLDevBlogTDD както следва:

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

Сега премахнете тестовия код, който сме написали в процедурата за настройка от предишния единичен тест, за да проверите ArticlesPerAuthorReport извежда, както следва:

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

Забележка :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.

Заключение

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

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

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

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


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Пренаписване на заявки за подобряване на производителността

  2. Преминете към стартиране на разработване на база данни, управлявано от тестове (TDDD)

  3. NextForm v3:Пет опции за миграция на данни и бази данни

  4. „Тайно ли е? Безопасно ли е?" Работа с чувствителни данни във вашето моделиране на данни

  5. Топ 3 съвета, които трябва да знаете, за да пишете по-бързи SQL изгледи