Като правило започваме да разработваме решения за бази данни, като създаваме обекти на база данни, като таблици, изгледи, съхранени процедури и т.н., базирани на бизнес изисквания. Този подход е известен още като Конвенционална разработка на бази данни . В тази статия ще проучим този подход и ще го илюстрираме с примери.
Конвенционална разработка на бази данни
Стилът на разработка се състои от следните стъпки:
- Получете изискванията
- Създавайте обекти на база данни въз основа на изисквания
- Изпълнете модульни тестове за обекти на база данни, за да видите дали отговарят на изискванията
- Получаване на нови изисквания
- Променете съществуващи обекти на база данни или добавете нови, за да отговарят на новите изисквания
- Създайте и стартирайте модулни тестове, за да проверите дали новите изисквания работят съответно и не са в конфликт с предишните
За да изследваме и илюстрираме процесите, нека започнем със създаване на примерна база данни 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
Според стандартите за тестване на база данни, управлявано от тестове, следните неща трябва да са налице, за да се изпълни изискването за докладване:
- Разработете един обект на база данни, който ще отговаря на изискването за отчитане.
- Създайте единичен тест, за да проверите съществуването на обекта.
- Създайте обект (заместител), за да преминете първия тест.
- Създайте втори единичен тест, за да проверите дали обектът извежда правилни данни в таблицата с правилно въвеждане.
- Променете дефиницията на обекта, за да позволите на втория тест да премине.
Създайте тест за единица на базата данни, за да проверите съществуването на желания обект
Ще присвоим обект на база данни, който може да отговори на бизнес изискването. В нашия случай това е 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!