Database
 sql >> Baza danych >  >> RDS >> Database

Skocz, aby rozpocząć opracowywanie baz danych oparte na testach (TDDD)

Z reguły tworzenie rozwiązań bazodanowych zaczynamy od tworzenia obiektów bazodanowych, takich jak tabele, widoki, procedury składowane itp., w oparciu o wymagania biznesowe. Takie podejście jest również znane jako konwencjonalne tworzenie baz danych . W tym artykule przyjrzymy się temu podejściu i zilustrujemy je przykładami.

Tworzenie konwencjonalnej bazy danych

Styl rozwoju składa się z następujących kroków:

  1. Odbierz wymagania
  2. Twórz obiekty bazy danych na podstawie wymagań
  3. Przeprowadź testy jednostkowe obiektów bazy danych, aby sprawdzić, czy spełniają wymagania
  4. Otrzymuj nowe wymagania
  5. Zmodyfikuj istniejące obiekty bazy danych lub dodaj nowe, aby spełnić nowe wymagania
  6. Tworzenie i uruchamianie testów jednostkowych, aby sprawdzić, czy nowe wymagania działają odpowiednio i nie kolidują z poprzednimi

Aby zbadać i zilustrować procesy, zacznijmy od skonfigurowania przykładowej bazy danych SQLDevBlog :

 
-- Create sample database (SQLDevBlog)
  CREATE DATABASE SQLDevBlog

Użyj poniższego kodu, aby utworzyć tabele w tej przykładowej bazie danych:

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

Przejrzyj diagram bazy danych z naszymi nowo utworzonymi tabelami:

Uwaga :Używam tutaj dbForge Studio dla SQL Server do wykonania wszystkich zadań. Wygląd jego danych wyjściowych może różnić się od SSMS (SQL Server Management Studio), ale wyniki są takie same.

Następnie wypełnimy nasz SQLDevBlog przykładowa baza danych, aby stworzyć bardziej realistyczny scenariusz:

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

W rezultacie mamy następujące wypełnione tabele:

Teraz, gdy skończyliśmy z konfiguracją bazy danych i zapełnianiem tabel, czeka nas kolejny krok. Musimy naśladować scenariusz z nowym wymaganiem.

Wymaganie dodania nowej kategorii

Nowe wymaganie stanowi, że administrator powinien mieć możliwość dodawania nowej kategorii do listy dostępnych kategorii . Aby spełnić to wymaganie, zespół programistów musi opracować procedurę składowaną umożliwiającą łatwe dodawanie nowego wymagania. Lub musimy utworzyć AddCategory Obiekt bazy danych.

Aby utworzyć procedurę składowaną, uruchom następujący skrypt:

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

Wynik jest następujący:

Utwórz test jednostkowy bazy danych, aby sprawdzić, czy procedura działa prawidłowo

Następnym krokiem jest utworzenie testu jednostkowego bazy danych, aby sprawdzić, czy procedura składowana spełnia specyfikację.

Ta wskazówka działa dla dbForge Studio dla SQL Server (lub tylko dbForge Test jednostkowy ) i SSMS (SQL Server Management Studio) . Uwaga:w przypadku korzystania z programu SSMS (SQL Server Management Studio) należy zainstalować tSQLt Framework do pisania testów jednostkowych.

Aby utworzyć pierwszy test jednostkowy bazy danych, kliknij prawym przyciskiem myszy SQLDevBlog baza danych> Test jednostkowy > Dodaj nowy test

Dodaj nowy test otwiera się okno. Podaj wszystkie wymagane informacje i kliknij Dodaj test .

Utwórz test jednostkowy w następujący sposób i zapisz go:

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

Kliknij Bazę danych menu> Test jednostkowy > Wyświetl listę testów i uruchom test jednostkowy, jak pokazano poniżej:

Widzimy, że test jednostkowy wypadł pomyślnie. W ten sposób do bazy danych (tabeli) można dodać nową kategorię. Wymóg został spełniony.

Teraz przyjrzymy się programowaniu baz danych opartemu na testach i opiszemy, w jaki sposób proces pisania testów jednostkowych może spełnić wymagania.

Testowe tworzenie baz danych (TDDD)

Test-driven database development (TDDD) rozpoczyna się od napisania testu jednostkowego, który jako pierwszy zakończy się niepowodzeniem. Następnie zmodyfikujemy go tak, aby przeszedł, a następnie udoskonalimy.

Testy jednostkowe są napisane w celu spełnienia wymagań i testów jednostkowych, które wymagają utworzenia i prawidłowego działania obiektów bazy danych.

Aby zrozumieć różnicę między tradycyjnym tworzeniem baz danych a tworzeniem baz danych opartym na testach, utwórzmy SQLDevBlogTDD Baza danych. To to samo co SQLDevBlog .

-- Create sample database (SQLDevBlogTDD)
  CREATE DATABASE SQLDevBlogTDD

Następnie wypełnij przykładową bazę danych tabelami:

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

Musimy wypełnić naszą przykładową bazę danych, aby stworzyć bardziej realistyczny scenariusz w następujący sposób:

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

Wymaganie dodania nowej kategorii (TDDD)

Teraz mamy to samo wymaganie:administrator powinien mieć możliwość dodania nowej kategorii do listy dostępnych kategorii. Aby spełnić to wymaganie, musimy najpierw napisać test jednostkowy bazy danych, który szuka potencjalnego obiektu.

Tak działa TDDD:najpierw test jednostkowy kończy się niepowodzeniem, ponieważ zakładamy, że szukamy obiektu, który obecnie nie istnieje, ale wkrótce tam będzie.

Utwórz i uruchom test jednostki bazy danych, aby sprawdzić, czy istnieje pożądany obiekt

Chociaż wiemy, że teraz nie istnieje, traktujemy to jako punkt wyjścia.

W dbForge Studio dla SQL Server test jednostkowy bazy danych, który domyślnie obsługuje TDDD, jest tworzony tak, aby najpierw się nie powiódł. Potem trochę to zmienimy. Jeśli używasz tSQLt Bezpośrednia struktura testów jednostkowych bazy danych, napisz następujący test jednostkowy:

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

Po uruchomieniu testu jednostkowego bazy danych widać, że test się nie powiódł:

Utwórz obiekt bazy danych i ponownie uruchom test jednostkowy

Następnym krokiem jest utworzenie wymaganego obiektu bazy danych. W naszym przypadku jest to procedura składowana.

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

Ponownie uruchom test jednostkowy – tym razem się powiedzie:

Nie wystarczy jednak przejść test jednostkowy, sprawdzając, czy procedura składowana istnieje. Musimy również sprawdzić, czy procedura składowana dodaje nową kategorię.

Utwórz test jednostki bazy danych, aby sprawdzić, czy rutyna działa prawidłowo

Utwórzmy nowy test jednostkowy bazy danych:

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

Jak widać, test jednostkowy kończy się niepowodzeniem za pierwszym razem i udaje się za drugim:

Dodaj funkcjonalność do rutynowego i ponownie uruchom test jednostkowy

Zmodyfikuj procedurę składowaną, dodając wymaganą funkcjonalność, aby test mógł się powieść, jak pokazano poniżej:

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

Uruchom ponownie testy jednostkowe, aby sprawdzić, czy wszystkie się powiodły, w tym ostatnio zmodyfikowaną procedurę składowaną:

W ten sposób z powodzeniem wdrożyliśmy testowe tworzenie baz danych. Teraz możemy skupić się tylko na wymaganiach. Testy jednostkowe hermetyzują wymagania, wymagając w ten sposób tworzenia i prawidłowego działania obiektów bazy danych w celu spełnienia specyfikacji.

Zobaczmy, jak skuteczne jest TDDD, jeśli chodzi o spełnienie wymogu raportowania biznesowego.

Spełnianie wymagań raportowania biznesowego przez TDDD

Zakładamy, że baza danych posiada już niezbędne obiekty (takie jak tabele) przed otrzymaniem nowego wymogu raportowania biznesowego.

Stwórzmy przykładową bazę danych o nazwie SQLDevBlogReportTDD :

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

Następnie utwórz i wypełnij tabele dla przykładowej bazy danych za pomocą następującego kodu:

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

Utwórz widok, aby zobaczyć listę wszystkich autorów, artykułów i kategorii artykułów:

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

Uruchom widok utworzonej tabeli, aby zobaczyć wyniki:

Po przetworzeniu konfiguracji bazy danych i wypełnieniu tabel następnym krokiem jest naśladowanie scenariusza, w którym otrzymaliśmy nowe wymaganie.

Wymaganie biznesowe:łączna liczba artykułów na autora raportu

Rozważ nowy wymóg raportowania biznesowego. Musi zawierać raport bazy danych, aby zobaczyć całkowitą liczbę artykułów na autora.

Pierwszą rzeczą jest przypisanie obiektu bazy danych, który spełni wymagania biznesowe. W naszym przypadku jest to ArticlesPerAuthorReport obiekt bazy danych.

Aby spełnić ten wymóg, konieczne jest stworzenie testu jednostkowego bazy danych, który wyszukuje potencjalnie odpowiedni obiekt. Jak wiemy, ten test najpierw się nie powiedzie, ponieważ będzie szukał obiektu, który w tej chwili nie istnieje, ale wkrótce tam będzie.

Plan wdrożenia TDDD

Zgodnie ze standardami testowania jednostkowego baz danych opartego na testach, aby spełnić wymóg raportowania, muszą istnieć następujące rzeczy:

  1. Opracuj pojedynczy obiekt bazy danych, który spełni wymagania raportowania.
  2. Utwórz test jednostkowy, aby sprawdzić istnienie obiektu.
  3. Utwórz skrót obiektu (symbol zastępczy), aby przejść pierwszy test.
  4. Utwórz drugi test jednostkowy, aby sprawdzić, czy obiekt wysyła poprawne dane do tabeli z poprawnymi danymi wejściowymi.
  5. Zmodyfikuj definicję obiektu, aby drugi test przeszedł.

Utwórz test jednostkowy bazy danych, aby sprawdzić istnienie pożądanego obiektu

Przypiszemy obiekt bazy danych, który spełni wymagania biznesowe. W naszym przypadku jest to ArticlesPerAuthorReport obiekt bazy danych. Następnym krokiem jest utworzenie testu jednostkowego bazy danych.

Aby utworzyć pierwszy test jednostkowy bazy danych, kliknij prawym przyciskiem myszy SQLDevBlogReport baza danych> Test jednostkowy > Dodaj nowy test

Napisz następujący kod, aby utworzyć test jednostkowy, który sprawdza istnienie lub brak pożądanego obiektu:

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

Test jednostkowy musi zakończyć się niepowodzeniem, ponieważ sprawdza utworzony wcześniej obiekt. Sam obiekt jest tworzony zgodnie z TDDD:

Utwórz skrót obiektu i uruchom jednostkę

Utwórz skrót obiektu z pewnym zakodowanym na stałe oczekiwanym wynikiem, ponieważ chcemy tylko utworzyć obiekt bazy danych z oczekiwanym wynikiem. Utwórz raport ArticlesPerAuthorReport obiekt jako fragment widoku (symbol zastępczy) na początku:

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

Uruchomienie testu jednostkowego powinno zakończyć się sukcesem:

Tworzenie kodu pośredniczącego służy jako kick-starter dla TDDD. Tworzymy obiekt, aby przejść test i nie przejmować się faktycznym funkcjonowaniem obiektu.

Utwórz i uruchom test jednostkowy, aby sprawdzić, czy dane wyjściowe obiektu są prawidłowe

Czas sprawdzić, czy żądany obiekt działa prawidłowo. Utwórz kolejny test jednostkowy, aby sprawdzić dane wyjściowe żądanego obiektu (ArticlesPerAuthorReport ). Dodajmy nowy test jednostkowy do bazy danych:

Dodaj następujący kod testu jednostkowego:

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

Uruchom test jednostkowy, który również musi się nie powieść, aby zachować zgodność z TDDD:

Dodaj wymaganą funkcjonalność do obiektu ArticlesPerAuthorReport

Test jednostkowy sprawdzający funkcjonalność obiektu wymaga zmodyfikowanej struktury, aby test mógł przejść.

Zmodyfikuj raport ArticlesPerAuthorReport widok, aby drugi test jednostkowy przeszedł tak samo jak pierwszy:

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

Obiekt bazy danych został pomyślnie zmodyfikowany w celu uzyskania żądanych danych. Uruchom wszystkie testy jednostkowe:

Raport ArticlesPerAuthorReport obiekt jest gotowy.

Naszym kolejnym zadaniem jest przedstawienie instruktażu tworzenia bazy raportów na podstawie obiektu bazy danych opracowanego i przetestowanego przy użyciu programowania sterowanego testami (TDDD).

Wdrażanie wymogu raportowania (ArticlesPerAuthorReport)

Najpierw zresetujemy SQLDevBlogReportTDD i dodaj do niego więcej danych. Możesz też po raz pierwszy utworzyć pustą bazę danych.

Aby dodać wystarczającą ilość danych do naszej przykładowej bazy danych, pobierz dane z vwAuthors zobacz, aby zobaczyć wszystkie zapisy:

Przeprowadzanie testów jednostkowych

Uruchom testy jednostkowe bazy danych, które stworzyliśmy wcześniej:

Gratulacje, oba testy przeszły pomyślnie, co oznacza, że ​​żądany obiekt bazy danych jest w stanie spełnić wymóg raportowania, aby wyświetlić artykuły według autora.

Utwórz raport bazy danych na podstawie obiektu ArticlesPerAuthorsReport

Raport bazy danych można utworzyć na wiele sposobów (np. używając Konstruktora raportów, tworząc projekt serwera raportów w Visual Studio Data Tools lub używając dbForge Studio dla SQL Server).

W tej sekcji do tworzenia raportów używamy dbForge Studio dla SQL Server. Aby kontynuować, kliknij Nowy z Plik Menu> Raport danych :

Kliknij Raport standardowy :

Wybierz Prosty stół\Widok jako Typ danych :

Dodaj obiekt bazowy (ArticlesPerAuthorReport ), czyli widok w naszym przypadku:

Dodaj wymagane pola:

W tym momencie nie potrzebujemy żadnego grupowania, więc kontynuuj, klikając Dalej :

Wybierz Układ i Orientacja raportu danych:

Na koniec dodaj tytuł Artykuły według raportu autora i kliknij Zakończ :

Następnie dostosuj formatowanie raportu zgodnie z wymaganiami:

Kliknij Podgląd aby zobaczyć raport bazy danych:

Zapisz raport jako ArticlesPerAuthorReport . Raport bazy danych został utworzony ze względu na wymagania biznesowe.

Korzystanie z procedury konfiguracji

Kiedy piszesz testy jednostkowe bazy danych za pomocą tSQLt, zobaczysz, że niektóre kody testowe są często powtarzane. W ten sposób można go zdefiniować w procedurze konfiguracji i ponownie wykorzystać w innych testach jednostkowych tej konkretnej klasy testowej. Każda klasa testowa może mieć tylko jedną procedurę konfiguracji, która uruchamia się automatycznie przed procesami testów jednostkowych tej klasy.

Możemy umieścić prawie cały kod testowy napisany w Assemble (sekcja) w ramach procedury konfiguracji, aby uniknąć powielania kodu.

Na przykład musimy stworzyć wykpiwane tabele wraz z oczekiwaną tabelą. Następnie dodamy dane do mocowanych tabel, a następnie do oczekiwanej tabeli. Możemy go łatwo zdefiniować w ramach procedury konfiguracji i ponownie wykorzystać.

Tworzenie procedury konfiguracji, aby uniknąć powielania kodu testowego

Utwórz procedurę składowaną w SQLDevBlogTDD w następujący sposób:

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

Teraz usuń kod testowy, który napisaliśmy w procedurze konfiguracji z poprzedniego testu jednostkowego, aby sprawdzić ArtykułyPerAuthorReport wyjścia w następujący sposób:

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

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

Wniosek

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. Zatrzask APPEND_ONLY_STORAGE_INSERT_POINT

  2. Pytania do wywiadu z inżynierem danych w Pythonie

  3. Rozszerzone wydarzenia dla SSAS

  4. Zawsze włączone grupy dostępności SQL:obiekty komputerowe

  5. Jak przekonwertować ciąg na wielkie litery w SQL?