Som regel begynder vi at udvikle databaseløsninger ved at lave databaseobjekter, såsom tabeller, visninger, lagrede procedurer osv. baseret på forretningskrav. Denne tilgang er også kendt som konventionel databaseudvikling . I denne artikel skal vi udforske denne tilgang og illustrere den med eksempler.
Konventionel databaseudvikling
Udviklingsstilen består af følgende trin:
- Modtag kravene
- Opret databaseobjekter baseret på krav
- Kør enhedstests for databaseobjekter for at se, om de opfylder kravene
- Modtag nye krav
- Rediger eksisterende databaseobjekter eller tilføj nye for at opfylde de nye krav
- Opret og kør enhedstests for at kontrollere, om nye krav fungerer i overensstemmelse hermed og ikke er i konflikt med de tidligere
For at udforske og illustrere processerne, lad os starte med at opsætte en eksempeldatabase SQLDevBlog :
-- Opret eksempeldatabase (SQLDevBlog) OPRET DATABASE SQLDevBlog
Brug følgende kode til at oprette tabeller i den eksempeldatabase:
BRUG SQLDevBlog;-- (1) Opret forfattertabel i eksempeldatabasenCREATE TABLE Forfatter ( AuthorId INT PRIMARY KEY IDENTITY (1, 1) ,Navn VARCHAR(40) ,RegistrationDate DATETIME2 ,Noter VARCHAR(400)) -- (2) Opret artikelkategoritabel i eksempeldatabasenCREATE TABLE Kategori ( CategoryId INT PRIMARY KEY IDENTITY (1, 1) ,Navn VARCHAR(50) ,Noter VARCHAR(400))-- (3) Opret artikeltabel i prøven databaseCREATE TABEL Artikel ( ArticleId INT PRIMÆR NØGLEIDENTITET (1, 1) ,CategoryId INT ,AuthorId INT ,Titel VARCHAR(150) ,Publiceret DATETIME2 ,Noter VARCHAR(400) )-- Tilføjelse af fremmednøgler til forfatter og TABSTRAINT artikel FK_Category_CategoryId UDENLANDSKE NØGLE (CategoryId) REFERENCER Kategori (CategoryId)ÆNDRINGSTABEL Artikel ADD CONSTRAINT FK_Author_AuthorId UDENLANDSKE NØGLE (AuthorId) REFERENCER Forfatter (AuthorId)GO
Gennemgå databasediagrammet med vores nyoprettede tabeller:
Bemærk :Jeg bruger her dbForge Studio til SQL Server til at udføre alle opgaver. Udseendet på dets output kan afvige fra SSMS (SQL Server Management Studio), men resultaterne er de samme.
Dernæst udfylder vi vores SQLDevBlog eksempeldatabase for at skabe et mere realistisk scenarie:
-- (5) Udfyldende forfattertabelINDSÆT I forfatter (navn, registreringsdato, noter) VÆRDIER ('Sam', '2017-01-01', 'Database Analyst'), ('Asif', '2017 -01-02', 'Database and Business Intelligence Developer'), ('Sadaf', '2018-01-01', 'Database Analyst Programmer')-- (6) Indfyldning af kategoritabelINDSÆT I Kategori (navn, noter) VÆRDIER ('Udvikling', 'Artikler om databaseudvikling'), ('Test', 'Databasetestrelaterede artikler'), ('DLM', 'Databaselivscyklusstyring')-- (7) Udfyldning af artikel 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, 'Alt om databasetestning', '03-01-2018', '');GO
Som et resultat har vi følgende udfyldte tabeller:
Nu hvor vi er færdige med databaseopsætning og tabelpopulation, står vi over for det næste trin. Vi er nødt til at efterligne scenariet med et nyt krav.
Kravet for at tilføje en ny kategori
Et nyt krav angiver, at en administrator skal være i stand til at føje en ny kategori til listen over tilgængelige kategorier . For at opfylde dette krav skal dit udviklingsteam komme med en lagret procedure for nemt at tilføje et nyt krav. Eller vi skal oprette en AddCategory Databaseobjekt.
For at oprette den lagrede procedure skal du køre følgende script:
-- (8) Denne procedure opfylder et nyt krav ved at tilføje en ny kategoriCREATE PROCEDURE dbo.AddCategory @CategoryName VARCHAR(50),@Notes VARCHAR(400)AS INSERT INTO Category (Name, Notes) VALUES ( @CategoryName, @Notes);GO
Resultatet er som følger:
Opret databaseenhedstest for at kontrollere, om proceduren fungerer korrekt
Det næste trin er at oprette databaseenhedstesten for at kontrollere, om den lagrede procedure opfylder specifikationen.
Dette tip virker for dbForge Studio til SQL Server (eller kun ). dbForge Unit Test ) og SSMS (SQL Server Management Studio) . Bemærk:Når du bruger SSMS (SQL Server Management Studio), skal du sørge for at installere tSQLt Ramme til at skrive enhedstestene.
For at oprette den første databaseenhedstest skal du højreklikke på SQLDevBlog database> Enhedstest > Tilføj ny test
Tilføj ny test vindue åbnes. Udfyld alle de nødvendige oplysninger, og klik på Tilføj test .
Opret enhedstesten som følger, og gem den:
-- Kommentarer her er knyttet til testen.-- For eksempler på testcase, se:http://tsqlt.org/user-guide/tsqlt-tutorial/CREATE PROCEDURE AddCategoryTests.[test for at kontrollere om AddCategory-proceduren virker]ASBEGIN --Assemble EXEC tSQLt.FakeTable @TableName ='dbo.Category' -- opret en tom afhængighedsfri kategoritabel CREATE TABLE AddCategoryTests.Expected ( -- opret forventet tabel CategoryId INT ,Name VARNULL , 50) . Bemærkninger VARCHAR(400) NULL ) INSERT INTO AddCategoryTests.Expected (CategoryId,Name, Notes) -- Indsæt data i forventede tabel VALUES (null,'Database Dummy Category', 'Dette er kun en dummy-kategori til test'); --Act EXEC AddCategory @CategoryName ='Database Dummy Category' ,@Notes ='Dette er kun en dummay-kategori til test' --Assert EXEC tSQLt.AssertEqualsTable @Expected ='AddCategoryTests.Expected' ,@Actualgory ' END;GO
Klik på databasen menu> Enhedstest > Se testliste og kør enhedstesten som vist nedenfor:
Vi kan se, at enhedstesten er vellykket. Der kan således tilføjes en ny kategori til databasen (tabel). Kravet er opfyldt.
Nu skal vi udforske testdrevet databaseudvikling og beskrive, hvordan processen med at skrive enhedstests kan opfylde kravene.
Testdrevet databaseudvikling (TDDD)
Testdrevet databaseudvikling (TDDD) begynder med at skrive den enhedstest, der vil mislykkes først. Derefter ændrer vi det, så det passer, og forfiner det derefter.
Enhedstest er skrevet for at opfylde krav og enhedstest, der kræver, at databaseobjekter oprettes og køres korrekt.
For at forstå forskellen mellem traditionel databaseudvikling og testdrevet databaseudvikling, lad os oprette SQLDevBlogTDD database. Det er det samme som SQLDevBlog .
-- Opret eksempeldatabase (SQLDevBlogTDD) OPRET DATABASE SQLDevBlogTDD
Udfyld derefter eksempeldatabasen med tabeller:
BRUG SQLDevBlogTDD;-- (1) Opret forfattertabel i eksempeldatabasenCREATE TABLE Forfatter ( AuthorId INT PRIMARY KEY IDENTITY (1, 1) ,Navn VARCHAR(40) ,Registrationsdato DATETIME2 ,Noter VARCHAR(400)) -- (2) Opret artikelkategoritabel i eksempeldatabasenCREATE TABLE Kategori ( CategoryId INT PRIMARY KEY IDENTITY (1, 1) ,Navn VARCHAR(50) ,Noter VARCHAR(400))-- (3) Opret artikeltabel i prøven databaseCREATE TABEL Artikel ( ArticleId INT PRIMÆR NØGLEIDENTITET (1, 1) ,CategoryId INT ,AuthorId INT ,Titel VARCHAR(150) ,Publiceret DATETIME2 ,Noter VARCHAR(400) )-- Tilføjelse af fremmednøgler til forfatter og TABSTRAINT artikel FK_Category_CategoryId UDENLANDSKE NØGLE (CategoryId) REFERENCER Kategori (CategoryId)ÆNDRINGSTABEL Artikel ADD CONSTRAINT FK_Author_AuthorId UDENLANDSKE NØGLE (AuthorId) REFERENCER Forfatter (AuthorId)GO
Vi er nødt til at udfylde vores prøvedatabase for at skabe et mere realistisk scenarie som følger:
-- Brug SQLDevBlogTDD-- (5) Udfyldende forfattertabelINDSÆT I forfatter (navn, registreringsdato, noter) VÆRDIER ('Sam', '2017-01-01', 'Databaseanalytiker'), ('Asif ', '2017-01-02', 'Database- og Business Intelligence-udvikler'), ('Sadaf', '2018-01-01', 'Databaseanalytikerprogrammør')-- (6) Indfyldning af kategoritabelINDSÆT I kategori (navn) , Noter) VALUES ('Udvikling', 'Artikler om databaseudvikling'), ('Test', 'Databasetestrelaterede artikler'), ('DLM', 'Databaselivscyklusstyring')-- (7) Udfyldning af artikel INSERT INTO Artikel (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, 'Alt om databasetestning', '03-01-2018', '');GO
Krav for at tilføje ny kategori (TDDD)
Nu har vi det samme krav:administratoren skal være i stand til at tilføje en ny kategori til listen over tilgængelige kategorier. For at opfylde kravet skal vi først skrive en databaseenhedstest, der leder efter et potentielt objekt.
Det er sådan TDDD fungerer:enhedstesten mislykkes først, da vi antager, at vi leder efter objektet, som ikke eksisterer i øjeblikket, men det vil snart være der.
Opret og kør testen af databaseenhed for at kontrollere, at det ønskede objekt eksisterer
Selvom vi ved, at det ikke eksisterer nu, tænker vi på dette som et udgangspunkt.
I dbForge Studio til SQL Server oprettes databaseenhedstesten, der understøtter TDDD som standard, til at mislykkes først. Så vil vi ændre det lidt. Hvis du bruger en tSQLt database unit test framework direkte, skriv følgende unit test:
-- Kommentarer her er knyttet til testen.-- For eksempler på testcase, se:http://tsqlt.org/user-guide/tsqlt-tutorial/CREATE PROCEDURE CategoryTests.[test for at kontrollere, om rutine til at tilføje ny kategori findes]ASBEGIN --Samle -- Denne sektion er til kode, der opsætter miljøet. Den indeholder ofte kald til metoder såsom tSQLt.FakeTable og tSQLt.SpyProcedure - sammen med INSERTs af relevante data. -- For mere information, se http://tsqlt.org/user-guide/isolating-dependencies/ --Act -- Udfør koden under test som en lagret procedure, funktion eller visning -- og indfang resultaterne i variabler eller borde. --Assert -- Sammenlign de forventede og faktiske værdier, eller kald tSQLt.Fail i en IF-sætning. -- Tilgængelige asserts:tSQLt.AssertEquals, tSQLt.AssertEqualsString, tSQLt.AssertEqualsTable -- For en komplet liste, se:http://tsqlt.org/user-guide/assertions/ EXEC tSQLt.AssertObjectExist @N'ObjectExist. AddCategory' END;GO
Når du har kørt databaseenhedstesten, kan du se, at testen mislykkes:
Opret databaseobjektet og kør enhedstesten igen
Det næste trin er at oprette det nødvendige databaseobjekt. I vores tilfælde er det en lagret procedure.
-- (8) Denne procedure opfylder det nye krav ved at tilføje en ny kategoriCREATE PROCEDURE dbo.AddCategory @CategoryName VARCHAR(50),@Notes VARCHAR(400)AS -- Category Procedure Stub (skabelon) i TDDDGO
Kør enhedstesten igen – denne gang lykkes det:
Men det er ikke nok at bestå enhedstesten, hvis den lagrede procedure eksisterer. Vi skal også kontrollere, om den lagrede procedure tilføjer en ny kategori.
Opret databaseenhedstesten for at kontrollere, om rutinen fungerer korrekt
Lad os oprette den nye databaseenhedstest:
-- Kommentarer her er knyttet til testen.-- For eksempler på testcase, se:http://tsqlt.org/user-guide/tsqlt-tutorial/CREATE PROCEDURE CategoryTests.[test for at kontrollere rutine tilføjer ny kategori]ASBEGIN --Samle EXEC tSQLt.FakeTable @TableName ='dbo.Category' -- create en tom afhængighedsfri kategoritabel CREATE TABLE CategoryTests.Expected ( -- opret forventet tabel CategoryId INT ,Name VARCHAR(50) NULL ,Notes VARCHAR(400) NULL ) INSERT INTO CategoryTests.Expected (CategoryId,Name, Notes) --Indsæt data ind i forventede tabel VALUES (null, 'Database Dummy Category', 'Dette er kun en dummy kategori til test'); --Act EXEC AddCategory @CategoryName ='Database Dummy Category' ,@Notes ='Dette er kun en dummay-kategori til test' --SELECT * INTO CategoryTests.Actual FROM Category -- sæt kategoritabeldata ind i en faktisk tabel --Assert EXEC tSQLt.AssertEqualsTable @Expected ='CategoryTests.Expected' ,@Actual ='dbo.Category' END;GO
Som du kan se, mislykkes enhedstesten for første gang og lykkes for anden gang:
Tilføj funktionalitet til rutine- og genkørselsenhedstesten
Rediger lagret procedure ved at tilføje den nødvendige funktionalitet, så testen kan lykkes som vist nedenfor:
-- (8) Denne procedure opfylder det nye krav ved at tilføje en ny kategoriALTER PROCEDURE dbo.AddCategory @CategoryName VARCHAR(50),@Notes VARCHAR(400)AS INSERT INTO Category (Name, Notes) VALUES ( @CategoryName, @Notes);GO
Kør enhedstestene igen for at kontrollere, at de alle lykkes, inklusive den nyligt ændrede lagrede procedure:
På denne måde har vi med succes implementeret testdrevet databaseudvikling. Nu kan vi kun fokusere på krav. Enhedstestene indkapsler krav og kræver derved, at databaseobjekter skal oprettes og køres korrekt for at opfylde specifikationen.
Lad os se, hvor effektiv TDDD er, når det kommer til at opfylde et krav til virksomhedsrapportering.
Opfyldelse af forretningsrapporteringskrav gennem TDDD
Vi antager, at databasen allerede har de nødvendige objekter (såsom tabeller), før den modtog det nye krav til virksomhedsrapportering.
Lad os oprette en eksempeldatabase kaldet SQLDevBlogReportTDD :
-- Opret eksempeldatabase (SQLDevBlogReportTDD)OPRET DATABASE SQLDevBlogReportTDD;GO
Opret og udfyld derefter tabellerne for eksempeldatabasen ved hjælp af følgende kode:
BRUG SQLDevBlogReportTDD;-- (1) Opret forfattertabel i eksempeldatabasenCREATE TABLE Author ( AuthorId INT PRIMARY KEY IDENTITY (1, 1) , Navn VARCHAR(40) , Registreringsdato DATETIME2 ,Noter VARCHAR(400)) -- (2) Opret en artikelkategoritabel i eksempeldatabasenCREATE TABLE Category ( CategoryId INT PRIMARY KEY IDENTITY (1, 1) ,Navn VARCHAR(50) ,Noter VARCHAR(400))-- (3) Opret artikeltabellen i eksempeldatabaseCREATE TABLE Artikel ( ArticleId INT PRIMARY KEY IDENTITY (1, 1) ,CategoryId INT ,AuthorId INT ,Titel VARCHAR(150) ,Publiceret DATETIME2 ,Noter VARCHAR(400) )-- Tilføjelse af artikelkategori ADD TABLE artiklenøgler for forfatter og TABLE BEGRÆNSNING FK_Category_CategoryId UDENLANDSKE NØGLE (CategoryId) REFERENCER Kategori (CategoryId)ÆNDRINGSTABEL Artikel ADD CONSTRAINT FK_Author_AuthorId UDENLANDSKE NØGLE (AuthorId) REFERENCER Forfatter (AuthorId) INGO-forfatter-tabel (AuthorId) INGO-(AuthorId) Registration (AuthorId) Registration Author-(VÆRDI) ', '2017-01-01', 'Databaseanalytiker'), ('Adil', '2017-01-02', 'Database and Business Intelligence Developer'), ('Sarah', '2018-01-01', 'Database Analyst Programmer'), ('Asim', '2018- 01-01', 'Database Analyst')-- (5) Indfyldning af kategoritabelINDSÆT I Kategori (navn, noter) VÆRDIER ('Analyse', 'Databaseanalyse'), ('Udvikling', 'Artikler om databaseudvikling'), ('Test', 'Databasetestrelaterede artikler'), ('DLM', 'Databaselivscyklusstyring') -- (6) Udfylder artikel INSERT INTO Article (CategoryId, AuthorId, Title, Published, Notes) VALUES (1, 1) , 'Replikere et problem i 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 , 'Enhedstest med tSQLt', '10-01-2018', '')GO
Opret en visning for at se listen over alle forfattere, artikler og artiklers kategorier:
-- (7) Opret en visning for at se en liste over forfattere, artikler og kategorier OPRET VISNING dbo.vwAuthors AS SELECT a.Name AS AuthorName,a1.Title AS ArticleTitle,c.Name AS CategoryName FROM Author a INNER JOIN Artikel a1 ON a.AuthorId =a1.AuthorId INNER JOIN Kategori c ON a1.CategoryId =c.CategoryIdGO
Kør visningen af den oprettede tabel for at se resultaterne:
Efter at have behandlet databaseopsætningen og udfyldt tabeller, er næste trin at efterligne scenariet, hvor vi modtog et nyt krav.
Forretningskrav:Samlet antal artikler pr. forfatterrapport
Overvej et nyt krav til virksomhedsrapportering. Den skal angive en databaserapport for at se det samlede antal artikler pr. forfatter.
Den første ting er at tildele et databaseobjekt, som kan opfylde forretningskrav. I vores tilfælde er det ArticlesPerAuthorReport databaseobjekt.
For at opfylde kravet er det nødvendigt at oprette en databaseenhedstest, der leder efter et potentielt passende objekt. Som vi ved, vil denne test mislykkes først, fordi den vil lede efter det objekt, der ikke eksisterer i øjeblikket, men som snart vil være der.
TDDD-implementeringsplan
I henhold til standarderne for testdrevet databaseenhedstest skal følgende ting være der for at opfylde rapporteringskravet:
- Udvikl et enkelt databaseobjekt, der opfylder rapporteringskravet.
- Opret en enhedstest for at kontrollere objektets eksistens.
- Opret en objektstub (pladsholder) for at bestå den første test.
- Opret en anden enhedstest for at kontrollere, om objektet udsender korrekte data i tabellen med korrekt input.
- Rediger objektdefinitionen for at lade den anden test bestå.
Opret databaseenhedstesten for at kontrollere, at det ønskede objekt eksisterer
Vi tildeler et databaseobjekt, som kan opfylde forretningskravet. I vores tilfælde er det ArticlesPerAuthorReport database objekt. Det næste trin er at oprette en databaseenhedstest.
For at oprette den første databaseenhedstest skal du højreklikke på SQLDevBlogReport database> Enhedstest > Tilføj ny test
Skriv følgende kode for at oprette enhedstesten, der kontrollerer eksistensen eller fraværet af det ønskede objekt:
OPRET PROCEDURE ArticlesPerAuthorReport.[test for at kontrollere ArticlesPerAuthorReport eksisterer]ASBEGIN --Assemble --Act --Assert EXEC tSQLt.AssertObjectExists @ObjectName =N'ArticlesPerAuthorReport'END;GO
Enhedstesten skal mislykkes, da den kontrollerer det objekt, der er oprettet før. Selve objektet er oprettet for at overholde TDDD:
Opret Object Stub og kør enheden
Opret en objektstub med noget hårdkodet forventet output, da vi blot vil oprette et databaseobjekt med det forventede resultat. Opret ArticlesPerAuthorReport objekt som en view stub (pladsholder) først:
-- (8) Create ArticlesPerAuthorReport view stub CREATE VIEW ArticlesPerAuthorReport AS VÆLG 'Adil' AS Forfatter, 10 AS [Samlede artikler] UNION ALLE VÆLG 'Sam' AS Forfatter, 5 AS [Samlede artikler]
Kørsel af enhedstesten skulle være vellykket:
Oprettelse af en stub fungerer som en kickstarter for TDDD. Vi opretter objektet for at bestå testen og bekymrer os ikke om den faktiske funktion af objektet.
Opret og kør enhedstesten for at kontrollere, om objektet udsender korrekte data
Det er tid til at kontrollere, om det ønskede objekt fungerer korrekt. Opret en anden enhedstest for at kontrollere for dataoutput fra det ønskede objekt (ArticlesPerAuthorReport ). Lad os tilføje en ny enhedstest til databasen:
Tilføj følgende enhedstestkode:
OPRET PROCEDURE ArticlesPerAuthorReport.[test for at kontrollere ArticlesPerAuthorReport-output korrekt]ASBEGIN --Assemble -- Opret mocked up-tabeller (blanke kopier af originale tabeller uden begrænsninger og 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' -- Tilføj rækker til de efterlignede tabeller 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, 'Avanceret databaseudvikling', DATEFROMPARTS(2017,02,01),'10K visninger'), (1,1, 1, 'Databaseudvikling med Cloud Technologies', DATEFROMPARTS(2017,02,01),'5K visninger'), (1,1, 1, 'Udvikling af databaser med moderne værktøjer', DATEFROMPARTS(2017,03,01),' 20.000 visninger'), (1,2, 2, 'Business Intelligence Fundamentals', DATEFROMPARTS(2017,02,01),'10.000 visninger'), (1,2, 2, 'Tabelmodeller', DATEFROMPARTS(2017,02 ,01),'50K visninger') -- Opret en forventet tabel OPRET TABEL ArticlesPerAuthorReport.Expected (Author VARCHAR(40),[Total Articles] int) -- Tilføj forventede resultater i en forventet tabel INSERT INTO ArticlesPerAuthorReport.Expected (Author, [Artikler i alt]) VÆRDIER ('Zak', 3), ('Akeel',2); --Act -- Kør ArticlesPerAuthorReport objekt (view) og indsæt resultater i en faktisk tabel SELECT * INTO ArticlesPerAuthorReport.Actual FROM ArticlesPerAuthorReport apar --Assert -- Sammenlign de forventede og faktiske tabeller EXEC TSQLT.AssertEqualsTable @Expected =N'ArticlesPer.ExAuthorReport. ' ,@Actual =N'ArticlesPerAuthorReport.Actual' END;GO
Kør enhedstesten, der også skal mislykkes for at overholde TDDD:
Tilføj påkrævet funktionalitet til ArticlesPerAuthorReport-objektet
Enhedstesten, der kontrollerer objektets funktionalitet, kræver en modificeret struktur, så testen kan bestå.
Rediger ArticlesPerAuthorReport view for at lade den anden enhedstest bestå ligesom den første:
ALTER VIEW ArticlesPerAuthorReport ASSELECT a.Name AS [Author],COUNT(a1.ArticleId) AS [Total Articles] FROM Author a INNER JOIN Artikel a1 PÅ a.AuthorId =a1.AuthorId GROUP BY a.Name
Databaseobjektet er blevet ændret til at udlæse ønskede data. Kør alle enhedstestene:
ArticlesPerAuthorReport objektet er klar.
Vores næste opgave er at give en gennemgang af oprettelse af en rapportbase på et databaseobjekt udviklet og testet ved hjælp af testdrevet udvikling (TDDD).
Implementering af rapporteringskrav (ArticlesPerAuthorReport)
Først skal vi nulstille SQLDevBlogReportTDD og tilføje flere data til det. Eller du kan oprette en tom database for første gang.
For at tilføje nok data til vores eksempeldatabase skal du hente data fra vwAuthors se for at se alle posterne:
Kørsel af enhedstestene
Kør databaseenhedstestene, som vi oprettede tidligere:
Tillykke, begge test er bestået, hvilket betyder, at det ønskede databaseobjekt er i stand til at opfylde rapporteringskravet for at se artikler pr. forfatter.
Opret databaserapport baseret på ArticlesPerAuthorsReport Object
Du kan oprette en databaserapport på mange måder (såsom ved at bruge Report Builder, oprette Report Server Project i Visual Studio Data Tools eller bruge dbForge Studio til SQL Server).
I dette afsnit bruger vi dbForge Studio til SQL Server til rapportoprettelse. Klik på Ny for at fortsætte fra Fil Menu> Datarapport :
Klik på Standardrapport :
Vælg Simpel tabel\visning som Datatype :
Tilføj basisobjektet (ArticlesPerAuthorReport ), hvilket er et synspunkt i vores tilfælde:
Tilføj de obligatoriske felter:
Vi har ikke brug for nogen gruppering på nuværende tidspunkt, så fortsæt ved at klikke på Næste :
Vælg Layout og Orientering af datarapporten:
Tilføj endelig titel Artikler pr. forfatterrapport og klik på Udfør :
Juster derefter formateringen af rapporten i henhold til kravet:
Klik på Vis eksempel for at se databaserapporten:
Gem rapporten som ArticlesPerAuthorReport . Databaserapporten er blevet oprettet på grund af forretningskravene.
Brug af opsætningsproceduren
Når du skriver databaseenhedstest ved hjælp af tSQLt, vil du se, at noget testkode gentages ofte. Du kan således definere det i en opsætningsprocedure og genbruge det efterfølgende i andre enhedstest af den pågældende testklasse. Hver testklasse kan kun have én opsætningsprocedure, der kører automatisk før enhedstesten af den pågældende klasses processer.
Vi kan placere næsten al testkode skrevet under Samle (afsnit) under opsætningsproceduren for at undgå kodeduplikering.
For eksempel skal vi oprette hånede tabeller sammen med en forventet tabel. Derefter tilføjer vi data til hånede tabeller og derefter til den forventede tabel. Vi kan nemt definere det under opsætningsproceduren og genbruge det yderligere.
Oprettelse af opsætningsprocedure for at undgå duplikering af testkode
Opret en lagret procedure i SQLDevBlogTDD som følger:
-- (12) Brug af opsætningsprocedure for at undgå gentagelse af almindelig testkode OPRET PROCEDURE ArticlesPerAuthorReport.Setup AS BEGIN --Assemble -- Opret mocked up tabeller (blanke kopier af originale tabeller uden begrænsninger og 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' -- Tilføj rækker til de hånede tabeller 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, 'Avanceret databaseudvikling', DATEFROMPARTS(2017,02,01),'10K visninger'), (1,1, 1, 'Databaseudvikling med Cloud Technologies', DATEFROMPARTS(2017,02,01),'5K visninger'), (1,1, 1, 'Udvikling af databaser med moderne værktøjer', DATEFROMPARTS(2017,03,01),' 20.000 visninger'), (1,2, 2, 'Business Intelligence Fundamentals', DATEFROMPARTS(2017,02,01),'10.000 visninger'), (1,2, 2, 'Tabelmodeller', DATEFROMPARTS(2017,02 ,01),'50K visninger') -- Opret en forventet tabel OPRET TABEL ArticlesPerAuthorReport.Expected (Author VARCHAR(40),[Total Articles] int) -- Tilføj forventede resultater i en forventet tabel INSERT INTO ArticlesPerAuthorReport.Expected (Author, [Artikler i alt]) VALUES ('Zak', 3), ('Akeel',2)END;GO
Fjern nu testkoden, vi har skrevet i opsætningsproceduren fra den tidligere enhedstest for at kontrollere ArticlesPerAuthorReport udgange som følger:
-- (11) Opret enhedstest check ArticlesPerAuthorReport outputs korrekteALTER PROCEDURE ArticlesPerAuthorReport.[test for at kontrollere ArticlesPerAuthorReport outputs korrekt]ASBEGIN --Assemble (Testkode skrevet i opsætningsproceduren) -- Opret mocked up tabeller (blanke kopier 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]ASBEGIN--Assemble--Act--AssertEXEC 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) stubCREATE PROCEDURE dbo.ArticlesPerAuthorByYearReport@Year INTASSELECT 'Adil' AS Author, 10 AS [Total Articles], 0000 AS [Year]UNION ALLSELECT '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. SetupASBEGIN--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 tablesINSERT 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 tableCREATE TABLE ArticlesPerAuthorByYearReport.Expected(Author VARCHAR(40),[Total Articles] INT,[Year] INT)-- Create an actual tableCREATE TABLE ArticlesPerAuthorByYearReport.Actual(Author VARCHAR(40),[Total Articles] INT,[Year] INT)-- Add expected results into an expected table for the year 2017INSERT 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 dataCREATE PROCEDURE ArticlesPerAuthorByYearReport.[test to check ArticlesPerAuthorByYearReport outputs correct data]ASBEGIN--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 tableINSERT INTO ArticlesPerAuthorByYearReport.ActualEXEC dbo.ArticlesPerAuthorByYearReport @Year=2017--Assert-- Compare the expected and actual tablesEXEC 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 yearCREATE PROCEDURE dbo.ArticlesPerAuthorByYearReport@Year INTASSELECTa.Name AS [Author],COUNT(a1.ArticleId) AS [Total Articles],YEAR(a.RegistrationDate) AS [Year]FROM Author aINNER JOIN Article a1ON a.AuthorId =a1.AuthorIdWHERE YEAR(a.RegistrationDate) =@YearGROUP BY a.Name,YEAR(a.RegistrationDate)GO
Bemærk :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.
Konklusion
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!