sql >> データベース >  >> RDS >> Database

ジャンプしてテスト駆動データベース開発(TDDD)を開始

    原則として、ビジネス要件に基づいて、テーブル、ビュー、ストアドプロシージャなどのデータベースオブジェクトを作成することにより、データベースソリューションの開発を開始します。このアプローチは、従来のデータベース開発とも呼ばれます。 。この記事では、このアプローチを検討し、例を挙げて説明します。

    従来のデータベース開発

    開発スタイルは、次の手順で構成されています。

    1. 要件を受け取る
    2. 要件に基づいてデータベースオブジェクトを作成する
    3. データベースオブジェクトの単体テストを実行して、要件を満たしているかどうかを確認します
    4. 新しい要件を受け取る
    5. 既存のデータベースオブジェクトを変更するか、新しい要件を満たすために新しいオブジェクトを追加します
    6. 単体テストを作成して実行し、新しい要件が適切に機能し、以前の要件と競合しないかどうかを確認します

    プロセスを調べて説明するために、サンプルデータベースのセットアップから始めましょう SQLDevBlog

     
    -- Create sample database (SQLDevBlog)
      CREATE DATABASE SQLDevBlog
    

    次のコードを使用して、そのサンプルデータベースにテーブルを作成します。

    USE SQLDevBlog;
    
    -- (1) Create Author table in the sample database
    CREATE TABLE Author (
      AuthorId INT PRIMARY KEY IDENTITY (1, 1)
     ,Name VARCHAR(40)
     ,RegistrationDate DATETIME2
     ,Notes VARCHAR(400)
    )
    
    -- (2) Create Article Category table in the sample database
    CREATE TABLE Category (
      CategoryId INT PRIMARY KEY IDENTITY (1, 1)
     ,Name VARCHAR(50)
     ,Notes VARCHAR(400)
    )
    
    -- (3) Create Article table in the sample database
    CREATE TABLE Article (
      ArticleId INT PRIMARY KEY IDENTITY (1, 1)
     ,CategoryId INT
     ,AuthorId INT
     ,Title VARCHAR(150)
     ,Published DATETIME2
     ,Notes VARCHAR(400)  
    )
    
    -- Adding foreign keys for author and article category
    ALTER TABLE Article ADD CONSTRAINT FK_Category_CategoryId FOREIGN KEY (CategoryId) REFERENCES Category (CategoryId)
    ALTER TABLE Article ADD CONSTRAINT FK_Author_AuthorId FOREIGN KEY (AuthorId) REFERENCES Author (AuthorId)
    
    GO
    

    新しく作成したテーブルを使用してデータベース図を確認します。

    :ここでは、dbForge Studio forSQLServerを使用してすべてのタスクを実行しています。出力の外観は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 for SQL Server(またはのみ)で機能します dbForgeユニットテスト およびSSMS(SQL Server Management Studio) 。注:SSMS(SQL Server Management Studio)を使用する場合は、必ず tSQLtをインストールしてください。 単体テストを作成するためのフレームワーク。

    最初のデータベース単体テストを作成するには、 SQLDevBlogを右クリックします。 データベース>ユニットテスト >新しいテストを追加

    新しいテストの追加 ウィンドウが開きます。必要な情報をすべて入力し、テストの追加をクリックします

    次のように単体テストを作成して保存します:

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

    データベースをクリックします メニュー>ユニットテスト >テストリストを表示 以下に示すように単体テストを実行します:

    単体テストが成功していることがわかります。したがって、新しいカテゴリをデータベース(テーブル)に追加できます。要件は満たされています。

    次に、テスト駆動型データベース開発を調査し、単体テストを作成するプロセスがどのように要件を満たすことができるかについて説明します。

    テスト駆動データベース開発(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 for 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
    

    ご覧のとおり、単体テストは初めて失敗し、2回目に成功します:

    ルーチンおよび再実行ユニットテストに機能を追加する

    以下に示すように、テストが成功するように、必要な機能を追加してストアドプロシージャを変更します。

    -- (8) This procedure meets the new requirement by adding a new category
    ALTER PROCEDURE dbo.AddCategory @CategoryName VARCHAR(50),
    @Notes VARCHAR(400)
    AS
      INSERT INTO Category (Name, Notes)
        VALUES (@CategoryName, @Notes);
    GO
    

    単体テストを再実行して、最近変更されたストアドプロシージャを含め、すべてが成功することを確認します。

    このようにして、テスト駆動型データベース開発の実装に成功しました。これで、要件のみに焦点を当てることができます。単体テストは要件をカプセル化するため、仕様を満たすためにデータベースオブジェクトを適切に作成して実行する必要があります。

    ビジネスレポーティングの要件を満たすことに関して、TDDDがどれほど効果的かを見てみましょう。

    TDDDによるビジネスレポート要件の充足

    新しいビジネスレポート要件を受け取る前に、データベースはすでに必要なオブジェクト(テーブルなど)を取得していると想定しています。

    SQLDevBlogReportTDDというサンプルデータベースを作成しましょう

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

    次に、次のコードを使用して、サンプルデータベースのテーブルを作成してデータを入力します。

    USE SQLDevBlogReportTDD;
    -- (1) Create Author table in the sample database
    CREATE TABLE Author (
      AuthorId INT PRIMARY KEY IDENTITY (1, 1)
     ,Name VARCHAR(40)
     ,RegistrationDate DATETIME2
     ,Notes VARCHAR(400)
    )
    
    -- (2) Create an Article Category table in the sample database
    CREATE TABLE Category (
      CategoryId INT PRIMARY KEY IDENTITY (1, 1)
     ,Name VARCHAR(50)
     ,Notes VARCHAR(400)
    )
    
    -- (3) Create Article table in the sample database
    CREATE TABLE Article (
      ArticleId INT PRIMARY KEY IDENTITY (1, 1)
     ,CategoryId INT
     ,AuthorId INT
     ,Title VARCHAR(150)
     ,Published DATETIME2
     ,Notes VARCHAR(400)  
    )
    
    -- Adding foreign keys for author and article category
    ALTER TABLE Article ADD CONSTRAINT FK_Category_CategoryId FOREIGN KEY (CategoryId) REFERENCES Category (CategoryId)
    ALTER TABLE Article ADD CONSTRAINT FK_Author_AuthorId FOREIGN KEY (AuthorId) REFERENCES Author (AuthorId)
    
    GO
    
    -- (4) Populating Author table
    INSERT INTO Author (Name, RegistrationDate, Notes)
      VALUES ('Peter', '2017-01-01', 'Database Analyst'),
      ('Adil', '2017-01-02', 'Database and Business Intelligence Developer'),
      ('Sarah', '2018-01-01', 'Database Analyst Programmer'),
      ('Asim', '2018-01-01', 'Database Analyst')
    
    -- (5) Populating Category table
    INSERT INTO Category (Name, Notes)
      VALUES 
      ('Analysis', 'Database Analysis'),
      ('Development', 'Articles about database development'),
      ('Testing', 'Database testing related articles'),
      ('DLM', 'Database lifecycle management')
     
    
    -- (6) Populating Article 
    INSERT INTO Article (CategoryId, AuthorId, Title, Published, Notes)
      VALUES (1, 1, 'Replicating a problem in SQL', '02-01-2018', ''),
      (1, 2, 'Modern Database Development Tools', '02-01-2018', ''),
      (3, 3, 'Test Driven Database Development (TDDD)', '03-01-2018', ''),
      (3, 1, 'Database Unit Testing Fundamentals', '10-01-2018', ''),
      (3, 3, 'Unit Testing with tSQLt', '10-01-2018', '')
    GO
    

    ビューを作成して、すべての著者、記事、および記事のカテゴリのリストを表示します。

    -- (7) Create a view to see a list of authors, articles, and categories
    CREATE VIEW dbo.vwAuthors 
    AS SELECT a.Name AS AuthorName,a1.Title AS ArticleTitle,c.Name AS CategoryName  FROM Author a INNER JOIN Article a1 ON a.AuthorId = a1.AuthorId INNER JOIN Category c ON a1.CategoryId = c.CategoryId
    GO
    

    作成したテーブルのビューを実行して、結果を確認します。

    データベースのセットアップを処理してテーブルにデータを入力したら、次のステップは、新しい要件を受け取ったシナリオを模倣することです。

    ビジネス要件:著者レポートあたりの記事の総数

    新しいビジネスレポートの要件を検討してください。著者ごとの記事の総数を表示するには、データベースレポートを記載する必要があります。

    まず、ビジネス要件を満たすことができるデータベースオブジェクトを割り当てることです。私たちの場合、それは ArticlesPerAuthorReport データベースオブジェクト。

    要件を満たすには、潜在的な適切なオブジェクトを探すデータベース単体テストを作成する必要があります。ご存知のとおり、このテストは、現時点では存在しないがまもなく存在するオブジェクトを検索するため、最初に失敗します。

    TDDD実装計画

    テスト駆動データベースの単体テストの標準によると、レポート要件を満たすには、次のものが必要です。

    1. レポート要件を満たす単一のデータベースオブジェクトを開発します。
    2. ユニットテストを作成して、オブジェクトの存在を確認します。
    3. 最初のテストに合格するためのオブジェクトスタブ(プレースホルダー)を作成します。
    4. 2番目の単体テストを作成して、オブジェクトが正しいデータを正しい入力でテーブルに出力するかどうかを確認します。
    5. オブジェクト定義を変更して、2番目のテストに合格させます。

    データベース単体テストを作成して、目的のオブジェクトが存在することを確認します

    ビジネス要件を満たすことができるデータベースオブジェクトを割り当てます。私たちの場合、それは ArticlesPerAuthorReport データベースオブジェクト。次のステップは、データベースの単体テストを作成することです。

    最初のデータベース単体テストを作成するには、 SQLDevBlogReportを右クリックします。 データベース>ユニットテスト >新しいテストを追加

    次のコードを記述して、目的のオブジェクトの有無をチェックする単体テストを作成します。

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

    ユニットテストは、以前に作成されたオブジェクトをチェックするため、失敗する必要があります。オブジェクト自体は、TDDDに準拠するように作成されています:

    オブジェクトスタブを作成してユニットを実行する

    期待される結果でデータベースオブジェクトを作成したいだけなので、ハードコードされた期待される出力でオブジェクトスタブを作成します。 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のキックスターターとして機能します。テストに合格するためにオブジェクトを作成し、オブジェクトの実際の機能については気にしません。

    単体テストを作成して実行し、オブジェクトが正しいデータを出力するかどうかを確認します

    目的のオブジェクトが正しく機能しているかどうかを確認するときが来ました。別の単体テストを作成して、目的のオブジェクトによって出力されたデータを確認します( 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を変更します 最初の単体テストと同じように、2番目の単体テストに合格するためのビュー:

    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オブジェクトに基づいてデータベースレポートを作成する

    データベースレポートはさまざまな方法で作成できます(レポートビルダーの使用、Visual Studioデータツールでのレポートサーバープロジェクトの作成、SQLServer用のdbForgeStudioの使用など)。

    このセクションでは、レポートの作成にdbForge Studio forSQLServerを使用しています。続行するには、[新規]をクリックします ファイルから メニュー>データレポート

    標準レポートをクリックします

    Simple Table \ View を選択します データ型として

    ベースオブジェクトを追加します( ArticlesPerAuthorReport )、これは私たちの場合のビューです:

    必須フィールドを追加します:

    この時点ではグループ化は必要ないため、次へをクリックして続行します

    レイアウトを選択します およびオリエンテーション データレポートの:

    最後に、タイトルを追加します著者レポートごとの記事 完了をクリックします

    次に、要件に従ってレポートのフォーマットを調整します。

    プレビューをクリックします データベースレポートを表示するには:

    レポートをArticlesPerAuthorReportとして保存します 。データベースレポートは、ビジネス要件のために作成されました。

    セットアップ手順の使用

    tSQLtを使用してデータベース単体テストを作成すると、いくつかのテストコードが頻繁に繰り返されることがわかります。したがって、セットアップ手順で定義し、後でその特定のテストクラスの他の単体テストで再利用できます。各テストクラスには、そのクラスプロセスの単体テストの前に自動的に実行されるセットアップ手順を1つだけ含めることができます。

    Assemblyで記述されたほとんどすべてのテストコードを配置できます (セクション)コードの重複を避けるためのセットアップ手順の下。

    たとえば、期待されるテーブルと一緒にモックテーブルを作成する必要があります。次に、モックされたテーブルにデータを追加し、次に期待されるテーブルにデータを追加します。セットアップ手順で簡単に定義し、さらに再利用できます。

    テストコードの重複を避けるためのセットアップ手順の作成

    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
    

    Note :If you are using a declarative database development tool like dbForge Studio for SQL Server, you’ll use the Create Procedure statement to modify the object. For tools like SSMS (SQL Server Management Studio), you must use ALTER Procedure

    Rerunning the database unit test for checking the proper object functioning gives us the following results:

    You have successfully unit tested the reporting procedure that is responsible for meeting the business requirement.

    結論

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

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

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

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


    1. NULLまたはISNULLのIN句

    2. 1つのクエリphpに複数のmysqlINSERTステートメント

    3. HikariCPPostgresqlドライバーがJDBCURLを受け入れないと主張

    4. SQL Serverにテーブルが存在するかどうかを確認する6つの方法(T-SQLの例)