チュートリアル: SQL Server 単体テストを作成して実行する

このチュートリアルでは、複数のストアド プロシージャの動作を検証する SQL Server の単体テストを作成します。 SQL Server の単体テストを作成すると、アプリケーションの不適切な動作の原因となる可能性があるコードの欠陥を特定するのに役立ちます。 SQL Server の単体テストとアプリケーション テストは、自動テスト スイートの一部として実行できます。

このチュートリアルでは、次のタスクを実行します。

ストアド プロシージャで単体テストの 1 つがエラーを検出した後、そのエラーを修正し、テストを再実行します。

前提条件

このチュートリアルを完了するには、データベースを作成および配置するための権限があるデータベース サーバー (LocalDB データベース) に接続できる必要があります。 詳しくは、「Visual Studio のデータベース機能に必要なアクセス許可」をご覧ください。

データベース スキーマを含むスクリプトを作成する

  1. [ ファイル ] メニューの [ 新規作成] をポイントし、[ ファイル] を選択します。

    [新しいファイル] ダイアログ ボックスが表示されます。

  2. [カテゴリ] ボックスの一覧で、[全般] を選択します (まだ強調表示されていない場合)。

  3. [テンプレート] ボックスの一覧で [Sql ファイル] を選択し、[開く] を選択します。

    Transact-SQL エディターが開きます。

  4. 次の Transact-SQL コードをコピーし、Transact-SQL エディターに貼り付けます。

    PRINT N'Creating Sales...';
    GO
    
    CREATE SCHEMA [Sales]
        AUTHORIZATION [dbo];
    GO
    
    PRINT N'Creating Sales.Customer...';
    GO
    
    CREATE TABLE [Sales].[Customer]
    (
        [CustomerID] INT IDENTITY (1, 1) NOT NULL,
        [CustomerName] NVARCHAR (40) NOT NULL,
        [YTDOrders] INT NOT NULL,
        [YTDSales] INT NOT NULL
    );
    GO
    
    PRINT N'Creating Sales.Orders...';
    GO
    
    CREATE TABLE [Sales].[Orders]
    (
        [CustomerID] INT NOT NULL,
        [OrderID] INT IDENTITY (1, 1) NOT NULL,
        [OrderDate] DATETIME NOT NULL,
        [FilledDate] DATETIME NULL,
        [Status] CHAR (1) NOT NULL,
        [Amount] INT NOT NULL
    );
    GO
    
    PRINT N'Creating Sales.Def_Customer_YTDOrders...';
    GO
    
    ALTER TABLE [Sales].[Customer]
        ADD CONSTRAINT [Def_Customer_YTDOrders] DEFAULT 0 FOR [YTDOrders];
    GO
    
    PRINT N'Creating Sales.Def_Customer_YTDSales...';
    GO
    
    ALTER TABLE [Sales].[Customer]
        ADD CONSTRAINT [Def_Customer_YTDSales] DEFAULT 0 FOR [YTDSales];
    GO
    
    PRINT N'Creating Sales.Def_Orders_OrderDate...';
    GO
    
    ALTER TABLE [Sales].[Orders]
        ADD CONSTRAINT [Def_Orders_OrderDate] DEFAULT GetDate() FOR [OrderDate];
    GO
    
    PRINT N'Creating Sales.Def_Orders_Status...';
    GO
    
    ALTER TABLE [Sales].[Orders]
        ADD CONSTRAINT [Def_Orders_Status] DEFAULT 'O' FOR [Status];
    GO
    
    PRINT N'Creating Sales.PK_Customer_CustID...';
    GO
    
    ALTER TABLE [Sales].[Customer]
        ADD CONSTRAINT [PK_Customer_CustID] PRIMARY KEY CLUSTERED ([CustomerID] ASC) WITH (ALLOW_PAGE_LOCKS = ON, ALLOW_ROW_LOCKS = ON, PAD_INDEX = OFF, IGNORE_DUP_KEY = OFF, STATISTICS_NORECOMPUTE = OFF);
    GO
    
    PRINT N'Creating Sales.PK_Orders_OrderID...';
    GO
    
    ALTER TABLE [Sales].[Orders]
        ADD CONSTRAINT [PK_Orders_OrderID] PRIMARY KEY CLUSTERED ([OrderID] ASC) WITH (ALLOW_PAGE_LOCKS = ON, ALLOW_ROW_LOCKS = ON, PAD_INDEX = OFF, IGNORE_DUP_KEY = OFF, STATISTICS_NORECOMPUTE = OFF);
    GO
    
    PRINT N'Creating Sales.FK_Orders_Customer_CustID...';
    GO
    
    ALTER TABLE [Sales].[Orders]
        ADD CONSTRAINT [FK_Orders_Customer_CustID] FOREIGN KEY ([CustomerID]) REFERENCES [Sales].[Customer] ([CustomerID]) ON DELETE NO ACTION ON UPDATE NO ACTION;
    GO
    
    PRINT N'Creating Sales.CK_Orders_FilledDate...';
    GO
    
    ALTER TABLE [Sales].[Orders]
        ADD CONSTRAINT [CK_Orders_FilledDate] CHECK ((FilledDate >= OrderDate)
                                                     AND (FilledDate < '01/01/2030'));
    GO
    
    PRINT N'Creating Sales.CK_Orders_OrderDate...';
    GO
    
    ALTER TABLE [Sales].[Orders]
        ADD CONSTRAINT [CK_Orders_OrderDate] CHECK ((OrderDate > '01/01/2005')
                                                    AND (OrderDate < '01/01/2030'));
    GO
    
    PRINT N'Creating Sales.uspCancelOrder...';
    GO
    
    CREATE PROCEDURE [Sales].[uspCancelOrder]
    @OrderID INT
    AS
    BEGIN
        DECLARE @Delta AS INT, @CustomerID AS INT;
        BEGIN TRANSACTION;
        SELECT @Delta = [Amount],
               @CustomerID = [CustomerID]
        FROM [Sales].[Orders]
        WHERE [OrderID] = @OrderID;
        UPDATE [Sales].[Orders]
            SET [Status] = 'X'
        WHERE [OrderID] = @OrderID;
        UPDATE [Sales].[Customer]
            SET YTDOrders = YTDOrders - @Delta
        WHERE [CustomerID] = @CustomerID;
        COMMIT TRANSACTION;
    END
    GO
    
    PRINT N'Creating Sales.uspFillOrder...';
    GO
    
    CREATE PROCEDURE [Sales].[uspFillOrder]
    @OrderID INT, @FilledDate DATETIME
    AS
    BEGIN
        DECLARE @Delta AS INT, @CustomerID AS INT;
        BEGIN TRANSACTION;
        SELECT @Delta = [Amount],
               @CustomerID = [CustomerID]
        FROM [Sales].[Orders]
        WHERE [OrderID] = @OrderID;
        UPDATE [Sales].[Orders]
            SET [Status]     = 'F',
                [FilledDate] = @FilledDate
        WHERE [OrderID] = @OrderID;
        UPDATE [Sales].[Customer]
            SET YTDSales = YTDSales - @Delta
        WHERE [CustomerID] = @CustomerID;
        COMMIT TRANSACTION;
    END
    GO
    
    PRINT N'Creating Sales.uspNewCustomer...';
    GO
    
    CREATE PROCEDURE [Sales].[uspNewCustomer]
    @CustomerName NVARCHAR (40)
    AS
    BEGIN
        INSERT INTO [Sales].[Customer] (CustomerName)
        VALUES (@CustomerName);
        RETURN SCOPE_IDENTITY();
    END
    GO
    
    PRINT N'Creating Sales.uspPlaceNewOrder...';
    GO
    
    CREATE PROCEDURE [Sales].[uspPlaceNewOrder]
    @CustomerID INT, @Amount INT, @OrderDate DATETIME, @Status CHAR (1)='O'
    AS
    BEGIN
        DECLARE @RC AS INT;
        BEGIN TRANSACTION;
        INSERT INTO [Sales].[Orders] (CustomerID, OrderDate, FilledDate, Status, Amount)
        VALUES (@CustomerID, @OrderDate, NULL, @Status, @Amount);
        SELECT @RC = SCOPE_IDENTITY();
        UPDATE [Sales].[Customer]
            SET YTDOrders = YTDOrders + @Amount
        WHERE [CustomerID] = @CustomerID;
        COMMIT TRANSACTION;
        RETURN @RC;
    END
    GO
    
    CREATE PROCEDURE [Sales].[uspShowOrderDetails]
    @CustomerID INT=0
    AS
    BEGIN
        SELECT [C].[CustomerName],
               CONVERT (DATE, [O].[OrderDate]),
               CONVERT (DATE, [O].[FilledDate]),
               [O].[Status],
               [O].[Amount]
        FROM [Sales].[Customer] AS C
             INNER JOIN [Sales].[Orders] AS O
                 ON [O].[CustomerID] = [C].[CustomerID]
        WHERE [C].[CustomerID] = @CustomerID;
    END
    GO
    
  5. ファイルを保存します。 次の手順でこのスクリプトを使用する必要があるため、この場所をメモしておきます。

  6. [ ファイル ] メニューの [ ソリューションを閉じる] を選択します。

    次に、データベース プロジェクトを作成し、作成したスクリプトからスキーマをインポートします。

データベース プロジェクトを作成してスキーマをインポートする

データベース プロジェクトを作成する

  1. [ ファイル ] メニューの [ 新規作成] をポイントし、[ プロジェクト] を選択します。

    [新しいプロジェクト] ダイアログ ボックスが表示されます。

  2. [インストールされたテンプレート] で、 [SQL Server] ノードを選択し、 [SQL Server データベース プロジェクト] を選択します。

  3. [ 名前] に「 SimpleUnitTestDB」と入力します。

  4. まだ選択されていない場合は、[ ソリューションのディレクトリの作成 ] チェック ボックスをオンにします。

  5. まだクリアされていない場合は、[ ソース管理に追加 ] チェック ボックスをオフにし、[ OK] を選択します

    データベース プロジェクトが作成され、 ソリューション エクスプローラーに表示されます。 次に、スクリプトからデータベース スキーマをインポートします。

スクリプトからデータベース スキーマをインポートする

  1. [ プロジェクト ] メニューの [ インポート ] を選択し、[ スクリプト (*.sql)] を選択します。

  2. [ようこそ] ページを読んだ後、[ 次へ ] を選択します。

  3. [ 参照] を選択し、 .sql ファイルを保存したディレクトリに移動します。

  4. .sql ファイルをダブルクリックし、[完了] を選択します。

    スクリプトがインポートされ、そのスクリプトで定義されているオブジェクトがデータベース プロジェクトに追加されます。

  5. 概要を確認し、[ 完了] を選択して操作を完了します。

    Note

    Sales.uspFillOrder プロシージャには、この手順の後半で検出して修正する意図的なコーディング エラーが含まれています。

結果のプロジェクトを調べる

  1. ソリューション エクスプローラーで、プロジェクトにインポートされたスクリプト ファイルを確認します。

  2. SQL Server オブジェクト エクスプローラーで、[プロジェクト] ノード内のデータベースを確認します。

LocalDB へのデプロイ

既定では、 F5 キーを押すと、LocalDB データベースにデータベースをデプロイ (または発行) します。 プロジェクトのプロパティ ページで、[デバッグ] タブに移動して接続文字列を変更すると、データベースの場所を変更できます。

SQL Server の単体テストを作成する

ストアド プロシージャの SQL Server 単体テストを作成する

  1. SQL Server オブジェクト エクスプローラーで、SimpleUnitTestDBのプロジェクト ノードを展開し、[プログラマビリティ] を展開し、[ストアド プロシージャ] ノードを展開します。

  2. いずれかのストアド プロシージャを右クリックし、[ 単体テストの作成 ] を選択して [ 単体テストの作成 ] ダイアログ ボックスを表示します。

  3. ストアド プロシージャのチェック ボックスの 5 つすべてを選択します。 Sales.uspCancelOrderSales.uspFillOrderSales.uspNewCustomerSales.uspPlaceNewOrderSales.uspShowOrderDetails

  4. [プロジェクト] ドロップダウン リストで、[新しい C# テスト プロジェクトの作成] を選択します。

  5. プロジェクト名とクラス名の既定の名前をそのまま使用し、[ OK] を選択します。

  6. テストを構成するダイアログ ボックスの [次のデータ接続を使用して単体テストを実行]で、このチュートリアルで既に配置したデータベースへの接続を指定します。 たとえば、既定のデプロイ場所 (LocalDB) を使用した場合は、[ 新しい接続 の指定 (LocalDB)]\[プロジェクト] を選択します。 次に、データベース名を選択します。 次に、[ OK] を 選択して [ 接続のプロパティ ] ダイアログ ボックスを閉じます。

    Note

    権限が制限されているビューまたはストアド プロシージャをテストする必要がある場合は、通常、この手順でその接続を指定します。 次に、テストを検証するために、権限の幅が広いセカンダリ接続を指定します。 セカンダリ接続がある場合は、そのユーザーをデータベース プロジェクトに追加し、デプロイ前スクリプトでそのユーザーのログインを作成する必要があります。

  7. テストを構成するダイアログ ボックスの [配置] セクションの [単体テストの実行前に自動的にデータベース プロジェクトを配置] チェック ボックスをオンにします。

  8. データベース プロジェクトで、SimpleUnitTestDB.sqlprojを選択します。

  9. [デプロイの構成] で、[デバッグ] を選択します

    テスト データは、SQL Server の単体テストの一部として生成することもできます。 このチュートリアルでは、テストで独自のデータが作成されるため、この手順はスキップします。

  10. [OK] を選択.

    テスト プロジェクトがビルドされ、SQL Server 単体テスト デザイナーが表示されます。 次に、単体テストの Transact-SQL スクリプトでテスト ロジックを更新します。

テスト ロジックを定義する

この基本データベースには、 CustomerOrderの 2 つのテーブルがあります。 データベースを更新するには、次のストアド プロシージャを使用します。

ストアド プロシージャ Description
uspNewCustomer このストアド プロシージャは、 Customer テーブルにレコードを追加します。これにより、顧客の YTDOrders 列と YTDSales 列が 0 に設定されます。
uspPlaceNewOrder このストアド プロシージャは、指定した顧客のOrders テーブルにレコードを追加し、YTDOrders テーブル内の対応するレコードのCustomer値を更新します。
uspFillOrder このストアド プロシージャは、状態を 'O' から 'F' に変更して、Orders テーブル内のレコードを更新し、YTDSales テーブル内の対応するレコードのCustomer量をインクリメントします。
uspCancelOrder このストアド プロシージャは、状態を 'O' から 'X' に変更して、Orders テーブル内のレコードを更新し、YTDOrders テーブル内の対応するレコードのCustomer量を減らします。
uspShowOrderDetails このストアド プロシージャは、 Orders テーブルを Custom テーブルと結合し、特定の顧客のレコードを表示します。

Note

この例では、基本的な SQL Server 単体テストを作成する方法を示します。 実際のデータベースでは、特定の顧客のステータスが 'O' または 'F' のすべての注文の総額を合計する場合があります。 また、このチュートリアルの手順には、エラー処理も含まれていません。 たとえば、既に処理済みの注文に対してuspFillOrderを呼び出すことを妨げることはありません。

テストでは、データベースが空の状態で開始されることを前提としています。 次の条件を確認するテストを作成します。

  • uspNewCustomer: ストアド プロシージャを実行した後、 Customer テーブルに 1 行が含まれていることを確認します。

  • uspPlaceNewOrder: CustomerID が 1 の顧客の場合は、100 ドルの注文を行います。 その顧客の YTDOrders 金額が 100 で、 YTDSales の金額がゼロであることを確認します。

  • uspFillOrder: CustomerID が 1 の顧客の場合は、50 ドルの注文を行います。 その注文を入力します。 YTDOrdersYTDSalesの両方の金額が 50 であることを確認します。

  • uspShowOrderDetails: CustomerID が 1 の顧客の場合は、$100、$50、および $5 の注文を行います。 uspShowOrderDetailsが正しい数の列を返し、結果セットに予想されるチェックサムがあることを確認します。

Note

SQL Server の単体テスト全体のセットでは、通常、その他の列が正しく設定されたことを確認します。 このチュートリアルを管理しやすいサイズに保つために、 uspCancelOrderの動作を検証する方法については説明しません。

uspNewCustomer の SQL Server 単体テストを記述する

  1. SQL Server 単体テスト デザイナーのナビゲーション バーで 、Sales_uspNewCustomerTestを選択し、隣接する一覧で テスト が強調表示されていることを確認します。

    前の手順を実行したら、単体テスト内のテスト アクション用のテスト スクリプトを作成できます。

  2. Transact-SQL エディターで、次のステートメントに一致するように Transact-SQL ステートメントを更新します。

    -- ssNoVersion unit test for Sales.uspNewCustomer
    DECLARE @RC AS INT, @CustomerName AS NVARCHAR (40);
    
    SELECT @RC = 0,
           @CustomerName = 'Fictitious Customer';
    
    EXECUTE
        @RC = [Sales].[uspNewCustomer]
        @CustomerName;
    
    SELECT *
    FROM [Sales].[Customer];
    
  3. [ テスト条件] ウィンドウで、[不確定テスト条件] を選択し、[ テスト条件の削除] アイコン (赤い X) を選択します。

  4. [ テスト条件 ] ウィンドウで、一覧で [行数 ] を選択し、[ テスト条件の追加 ] アイコン (緑色の +) を選択します。

  5. [プロパティ] ウィンドウを開き (テスト条件を選択して F4 キーを押します)、Row Count プロパティを 1 に設定します。

  6. [ファイル] メニューの [すべてを保存] をクリックします。

    次に、 uspPlaceNewOrderの単体テスト ロジックを定義します。

uspPlaceNewOrder の SQL Server 単体テストを記述する

  1. SQL Server 単体テスト デザイナーのナビゲーション バーで 、Sales_uspPlaceNewOrderTestを選択し、隣接する一覧で [テスト ] が強調表示されていることを確認します。

    この手順を実行したら、単体テスト内のテスト アクション用のテスト スクリプトを作成できます。

  2. Transact-SQL エディターで、次のステートメントに一致するように Transact-SQL ステートメントを更新します。

    -- ssNoVersion unit test for Sales.uspPlaceNewOrder
    DECLARE @RC AS INT, @CustomerID AS INT, @Amount AS INT, @OrderDate AS DATETIME, @Status AS CHAR (1);
    
    DECLARE @CustomerName AS NVARCHAR (40);
    
    -- NOTE: Assumes that you inserted a Customer record with CustomerName='Fictitious Customer' in the pre-test script.
    SELECT @RC = 0,
           @CustomerID = 0,
           @CustomerName = N'Fictitious Customer',
           @Amount = 100,
           @OrderDate = getdate(),
           @Status = 'O';
    
    SELECT @CustomerID = [CustomerID]
    FROM [Sales].[Customer]
    WHERE [CustomerName] = @CustomerName;
    
    -- place an order for that customer
    EXECUTE
        @RC = [Sales].[uspPlaceNewOrder]
        @CustomerID,
        @Amount,
        @OrderDate,
        @Status;
    
    -- verify that the YTDOrders value is correct.
    SELECT @RC = [YTDOrders]
    FROM [Sales].[Customer]
    WHERE [CustomerID] = @CustomerID;
    
    SELECT @RC AS RC;
    
  3. [ テスト条件 ] ウィンドウで、[不確定テスト条件] を選択し、[ テスト条件の削除] を選択します。

  4. [ テスト条件] ウィンドウで、一覧で [スカラー値 ] を選択し、[ テスト条件の追加] を選択します。

  5. [プロパティ] ウィンドウで、 [予期される値] プロパティを 100 に設定します。

  6. SQL Server 単体テスト デザイナーのナビゲーション バーで 、Sales_uspPlaceNewOrderTestを選択し、隣接する一覧で [事前テスト ] が強調表示されていることを確認します。

    この手順の実行後に、テストの実行に必要な状態にデータを変更するステートメントを指定できます。 この例では、注文を行う前に、 Customer レコードを作成する必要があります。

  7. [ ここをクリック] を選択して作成 し、事前テスト スクリプトを作成します。

  8. Transact-SQL エディターで、次のステートメントに一致するように Transact-SQL ステートメントを更新します。

    /*
                                  Add Transact-SQL statements here that you want to run before
                                  the test script is run.
                                  */
    
    -- Add a customer for this test with the name 'Fictitious Customer'
    DECLARE @NewCustomerID AS INT, @CustomerID AS INT, @RC AS INT, @CustomerName AS NVARCHAR (40);
    
    SELECT @RC = 0,
           @NewCustomerID = 0,
           @CustomerID = 0,
           @CustomerName = N'Fictitious Customer';
    
    IF NOT EXISTS (SELECT *
                   FROM [Sales].[Customer]
                   WHERE CustomerName = @CustomerName)
        BEGIN
                    EXECUTE
                @NewCustomerID = [Sales].[uspNewCustomer]
                @CustomerName;
        END
    -- NOTE: Assumes that you inserted a Customer record with CustomerName='Fictitious Customer' in the pre-test script.
    
    SELECT @CustomerID = [CustomerID]
    FROM [Sales].[Customer]
    WHERE [CustomerName] = @CustomerName;
    -- delete any old records in the Orders table and clear out the YTD Sales/Orders fields
    
    DELETE [Sales].[Orders]
    WHERE [CustomerID] = @CustomerID;
    
    UPDATE [Sales].[Customer]
        SET YTDOrders = 0,
            YTDSales  = 0
    WHERE [CustomerID] = @CustomerID;
    
  9. [ファイル] メニューの [すべてを保存] をクリックします。

    次に、 uspFillOrderの単体テストを作成します。

uspFillOrder の SQL Server 単体テストを記述する

  1. SQL Server 単体テスト デザイナーのナビゲーション バーで 、Sales_uspFillOrderTestを選択し、隣接する一覧で テスト が強調表示されていることを確認します。

    この手順を実行したら、単体テスト内のテスト アクション用のテスト スクリプトを作成できます。

  2. Transact-SQL エディターで、次のステートメントに一致するように Transact-SQL ステートメントを更新します。

    -- ssNoVersion unit test for Sales.uspFillOrder
    DECLARE @RC AS INT, @CustomerID AS INT, @Amount AS INT, @FilledDate AS DATETIME, @Status AS CHAR (1);
    
    DECLARE @CustomerName AS NVARCHAR (40), @OrderID AS INT;
    
    SELECT @RC = 0,
           @CustomerID = 0,
           @OrderID = 0,
           @CustomerName = N'Fictitious Customer',
           @Amount = 100,
           @FilledDate = getdate(),
           @Status = 'O';
    -- NOTE: Assumes that you inserted a Customer record with CustomerName='Fictitious Customer' in the pre-test script.
    
    SELECT @CustomerID = [CustomerID]
    FROM [Sales].[Customer]
    WHERE [CustomerName] = @CustomerName;
    -- Get the most recently added order.
    
    SELECT @OrderID = MAX([OrderID])
    FROM [Sales].[Orders]
    WHERE [CustomerID] = @CustomerID;
    -- fill an order for that customer
    
    EXECUTE
        @RC = [Sales].[uspFillOrder]
        @OrderID,
        @FilledDate;
    -- verify that the YTDOrders value is correct.
    
    SELECT @RC = [YTDSales]
    FROM [Sales].[Customer]
    WHERE [CustomerID] = @CustomerID;
    
    SELECT @RC AS RC;
    
  3. [ テスト条件 ] ウィンドウで、[不確定テスト条件] を選択し、[ テスト条件の削除] を選択します。

  4. [ テスト条件] ウィンドウで、一覧で [スカラー値 ] を選択し、[ テスト条件の追加] を選択します。

  5. [プロパティ] ウィンドウで、 [予期される値] プロパティを 100 に設定します。

  6. SQL Server 単体テスト デザイナーのナビゲーション バーで 、Sales_uspFillOrderTestを選択し、隣接する一覧で [事前テスト ] が強調表示されていることを確認します。 この手順の実行後に、テストの実行に必要な状態にデータを変更するステートメントを指定できます。 この例では、注文を行う前に Customer レコードを作成する必要があります。

  7. [ ここをクリック] を選択して作成 し、事前テスト スクリプトを作成します。

  8. Transact-SQL エディターで、次のステートメントに一致するように Transact-SQL ステートメントを更新します。

    /*
                                  Add Transact-SQL statements here that you want to run before
                                  the test script is run.
                                  */
    BEGIN TRANSACTION
    -- Add a customer for this test with the name 'CustomerB'
    ;
    
    DECLARE @NewCustomerID AS INT, @RC AS INT, @CustomerName AS NVARCHAR (40);
    
    SELECT @RC = 0,
           @NewCustomerID = 0,
           @CustomerName = N'Fictitious Customer';
    
    IF NOT EXISTS (SELECT *
                   FROM [Sales].[Customer]
                   WHERE CustomerName = @CustomerName)
        BEGIN
                    EXECUTE
                @NewCustomerID = [Sales].[uspNewCustomer]
                @CustomerName;
        END
    
    DECLARE @CustomerID AS INT, @Amount AS INT, @OrderDate AS DATETIME, @Status AS CHAR (1);
    
    SELECT @RC = 0,
           @CustomerID = 0,
           @CustomerName = N'Fictitious Customer',
           @Amount = 100,
           @OrderDate = getdate(),
           @Status = 'O';
    -- NOTE: Assumes that you inserted a Customer record with CustomerName='Fictitious Customer' in the pre-test script.
    
    SELECT @CustomerID = [CustomerID]
    FROM [Sales].[Customer]
    WHERE [CustomerName] = @CustomerName;
    -- delete any old records in the Orders table and clear out the YTD Sales/Orders fields
    
    DELETE [Sales].[Orders]
    WHERE [CustomerID] = @CustomerID;
    
    UPDATE [Sales].[Customer]
        SET YTDOrders = 0,
            YTDSales  = 0
    WHERE [CustomerID] = @CustomerID;
    
    EXECUTE
        @RC = [Sales].[uspPlaceNewOrder]
        @CustomerID,
        @Amount,
        @OrderDate,
        @Status;
    
    COMMIT TRANSACTION;
    
    -- place an order for that customer
    
  9. [ファイル] メニューの [すべてを保存] をクリックします。

uspShowOrderDetails の SQL Server 単体テストを記述する

  1. SQL Server 単体テスト デザイナーのナビゲーション バーで 、Sales_uspShowOrderDetailsTestを選択し、隣接する一覧で [テスト ] が強調表示されていることを確認します。

    この手順を実行したら、単体テスト内のテスト アクション用のテスト スクリプトを作成できます。

  2. Transact-SQL エディターで、次のステートメントに一致するように Transact-SQL ステートメントを更新します。

    -- ssNoVersion unit test for Sales.uspFillOrder
    DECLARE @RC AS INT, @CustomerID AS INT, @Amount AS INT, @FilledDate AS DATETIME, @Status AS CHAR (1);
    
    DECLARE @CustomerName AS NVARCHAR (40), @OrderID AS INT;
    
    SELECT @RC = 0,
           @CustomerID = 0,
           @OrderID = 0,
           @CustomerName = N'Fictitious Customer',
           @Amount = 100,
           @FilledDate = getdate(),
           @Status = 'O';
    -- NOTE: Assumes that you inserted a Customer record with CustomerName='Fictitious Customer' in the pre-test script.
    
    SELECT @CustomerID = [CustomerID]
    FROM [Sales].[Customer]
    WHERE [CustomerName] = @CustomerName;
    -- fill an order for that customer
    
    EXECUTE
        @RC = [Sales].[uspShowOrderDetails]
        @CustomerID;
    
    SELECT @RC AS RC;
    
  3. [ テスト条件 ] ウィンドウで、[不確定テスト条件] を選択し、[ テスト条件の削除] を選択します。

  4. [ テスト条件 ] ウィンドウで、一覧で [予期されるスキーマ ] を選択し、[ テスト条件の追加] を選択します。

  5. [ プロパティ ] ウィンドウの [構成 ] プロパティで、参照ボタン ('...') を選択します。

  6. [expectedSchemaCondition1 の構成] ダイアログ ボックスで、データベースへの接続を指定します。 たとえば、既定のデプロイ場所 (LocalDB) を使用した場合は、[ 新しい接続 の指定 (LocalDB)]\[プロジェクト] を選択します。 次に、データベース名を選択します。

  7. [ 取得] を選択します。 (必要に応じて、データが表示されるまで [取得 ] を選択します)。

    単体テストの Transact-SQL 本文が実行され、結果のスキーマがダイアログ ボックスに表示されます。 事前テスト コードは実行されていないため、データは返されません。 スキーマのみを検証し、データを検証していないので、これは問題ありません。

  8. [OK] を選択.

    必要なスキーマがテスト条件と共に格納されます。

  9. SQL Server 単体テスト デザイナーのナビゲーション バーで 、Sales_uspShowOrderDetailsTestを選択し、隣接する一覧で [事前テスト ] が強調表示されていることを確認します。 この手順の実行後に、テストの実行に必要な状態にデータを変更するステートメントを指定できます。 この例では、注文を行う前に、 Customer レコードを作成する必要があります。

  10. [ ここをクリック] を選択して作成 し、事前テスト スクリプトを作成します。

  11. Transact-SQL エディターで、次のステートメントに一致するように Transact-SQL ステートメントを更新します。

    /*
                                  Add Transact-SQL statements here to run before the test script is run.
                                  */
    BEGIN TRANSACTION
    -- Add a customer for this test with the name 'FictitiousCustomer'
    ;
    
    DECLARE @NewCustomerID AS INT, @RC AS INT, @CustomerName AS NVARCHAR (40);
    
    SELECT @RC = 0,
           @NewCustomerID = 0,
           @CustomerName = N'Fictitious Customer';
    
    IF NOT EXISTS (SELECT *
                   FROM [Sales].[Customer]
                   WHERE CustomerName = @CustomerName)
        BEGIN
                    EXECUTE
                @NewCustomerID = [Sales].[uspNewCustomer]
                @CustomerName;
        END
    
    DECLARE @CustomerID AS INT, @Amount AS INT, @OrderDate AS DATETIME, @Status AS CHAR (1);
    
    SELECT @RC = 0,
           @CustomerID = 0,
           @CustomerName = N'Fictitious Customer',
           @OrderDate = getdate(),
           @Status = 'O';
    -- NOTE: Assumes that you inserted a Customer record with CustomerName='Fictitious Customer' in the pre-test script.
    
    SELECT @CustomerID = [CustomerID]
    FROM [Sales].[Customer]
    WHERE [CustomerName] = @CustomerName;
    -- delete any old records in the Orders table and clear out the YTD Sales/Orders fields
    
    DELETE [Sales].[Orders]
    WHERE [CustomerID] = @CustomerID;
    
    UPDATE [Sales].[Customer]
        SET YTDOrders = 0,
            YTDSales  = 0
    WHERE [CustomerID] = @CustomerID;
    
    EXECUTE
        @RC = [Sales].[uspPlaceNewOrder]
        @CustomerID, 100,
        @OrderDate,
        @Status;
    
    EXECUTE
        @RC = [Sales].[uspPlaceNewOrder]
        @CustomerID, 50,
        @OrderDate,
        @Status;
    
    EXECUTE
        @RC = [Sales].[uspPlaceNewOrder]
        @CustomerID, 5,
        @OrderDate,
        @Status;
    
    COMMIT TRANSACTION;
    
    -- place 3 orders for that customer
    
  12. SQL Server 単体テスト デザイナーのナビゲーション バーで、 Sales_uspShowOrderDetailsTestを選択し、隣接する一覧で [テスト ] を選択します。

    この操作が必要なのは、チェックサムの条件を事前テストではなくテストに適用するためです。

  13. [ テスト条件 ] ウィンドウで、一覧で [データ チェックサム ] を選択し、[ テスト条件の追加] を選択します。

  14. [ プロパティ ] ウィンドウの [構成 ] プロパティで、参照ボタン ('...') を選択します。

  15. [checksumCondition1 の構成] ダイアログ ボックスで、データベースへの接続を指定します。

  16. ([接続の編集] をクリックすると表示される) ダイアログ ボックスで Transact-SQL を次のコードに置き換えます。

    BEGIN TRANSACTION
    -- Add a customer for this test with the name 'CustomerB'
    ;
    
    DECLARE @NewCustomerID AS INT, @RC AS INT, @CustomerName AS NVARCHAR (40);
    
    SELECT @RC = 0,
           @NewCustomerID = 0,
           @CustomerName = N'Fictitious Customer';
    
    IF NOT EXISTS (SELECT *
                   FROM [Sales].[Customer]
                   WHERE CustomerName = @CustomerName)
        BEGIN
                    EXECUTE
                @NewCustomerID = [Sales].[uspNewCustomer]
                @CustomerName;
        END
    
    DECLARE @CustomerID AS INT, @Amount AS INT, @OrderDate AS DATETIME, @Status AS CHAR (1);
    
    SELECT @RC = 0,
           @CustomerID = 0,
           @CustomerName = N'Fictitious Customer',
           @OrderDate = getdate(),
           @Status = 'O';
    -- NOTE: Assumes that you inserted a Customer record with CustomerName='Fictitious Customer' in the pre-test script.
    
    SELECT @CustomerID = [CustomerID]
    FROM [Sales].[Customer]
    WHERE [CustomerName] = @CustomerName;
    -- delete any old records in the Orders table and clear out the YTD Sales/Orders fields
    
    DELETE [Sales].[Orders]
    WHERE [CustomerID] = @CustomerID;
    
    UPDATE [Sales].[Customer]
        SET YTDOrders = 0,
            YTDSales  = 0
    WHERE [CustomerID] = @CustomerID;
    
    EXECUTE
        @RC = [Sales].[uspPlaceNewOrder]
        @CustomerID, 100,
        @OrderDate,
        @Status;
    
    EXECUTE
        @RC = [Sales].[uspPlaceNewOrder]
        @CustomerID, 50,
        @OrderDate,
        @Status;
    
    EXECUTE
        @RC = [Sales].[uspPlaceNewOrder]
        @CustomerID, 5,
        @OrderDate,
        @Status;
    
    COMMIT TRANSACTION;
    
    DECLARE @FilledDate AS DATETIME;
    
    DECLARE @OrderID AS INT;
    
    SELECT @RC = 0,
           @CustomerID = 0,
           @OrderID = 0,
           @CustomerName = N'Fictitious Customer',
           @Amount = 100,
           @FilledDate = getdate(),
           @Status = 'O';
    
    SELECT @CustomerID = [CustomerID]
    FROM [Sales].[Customer]
    WHERE [CustomerName] = @CustomerName;
    
    EXECUTE
        @RC = [Sales].[uspShowOrderDetails]
        @CustomerID;
    
    SELECT @RC AS RC;
    
    -- place 3 orders for that customer  -- ssNoVersion unit test for Sales.uspFillOrder  -- NOTE: Assumes that you inserted a Customer record with CustomerName='Fictitious Customer' in the pre-test script.  -- fill an order for that customer
    

    このコードは、事前テストの Transact-SQL コードとテスト自体の Transact-SQL コードを組み合わせたものです。 両方とも、テストの実行時に返されるのと同じ結果を返す必要があります。

  17. [ 取得] を選択します。 (必要に応じて、データが表示されるまで [取得 ] を選択します)。

    指定した Transact-SQL が実行され、返されたデータのチェックサムが計算されます。

  18. [OK] を選択.

    計算されたチェックサムがテスト条件と共に格納されます。 [データ チェックサム] テスト条件の [値] 列には、必要なチェックサムが表示されます。

  19. [ファイル] メニューの [すべてを保存] をクリックします。

    この時点で、テストを実行する準備ができました。

SQL Server の単体テストを実行する

SQL Server 単体テストを実行する

  1. [ テスト ] メニューで、 Windows をポイントし、Visual Studio 2010 の テスト ビュー または Visual Studio 2012 の テスト エクスプローラー を選択します。

  2. [テスト ビュー] ウィンドウ (Visual Studio 2010) で、ツール バーの [更新] を選択してテストの一覧を更新します。 テスト エクスプローラー (Visual Studio 2012) でテストの一覧を表示するには、ソリューションをビルドします。

    [テスト ビュー] ウィンドウまたは [テスト エクスプローラー] ウィンドウには、このチュートリアルの前の手順で作成し、Transact-SQL ステートメントとテスト条件を追加したテストの一覧が表示されます。 TestMethod1 という名前のテストは空であり、このチュートリアルでは使用されません。

  3. Sales_uspNewCustomerTestを右クリックし、[選択項目の実行] を選択します

    Visual Studio では、指定した特権コンテキストを使用してデータベースに接続し、データ生成計画を適用します。 Visual Studio は実行コンテキストに切り替わり、テストの Transact-SQL スクリプトが実行されます。 最後に、 Visual Studio は、 Transact-SQL スクリプトの結果を、テスト条件で指定した内容と照合して評価し、成功したか失敗したかという結果を [テスト結果] ウィンドウに表示します。

  4. [テスト結果] ウィンドウの結果を確認します。

    テストは成功します。つまり、 SELECT ステートメントは実行時に 1 行を返します。

  5. Sales_uspPlaceNewOrderTestSales_uspFillOrderTest、およびSales_uspShowOrderDetailsTestのテストに対して手順 3 を繰り返します。 結果は次のようになります。

    テスト 予測される結果
    Sales_uspPlaceNewOrderTest 合格
    Sales_uspShowOrderDetailsTest 合格
    Sales_uspFillOrderTest 次のエラーで失敗します。 ScalarValueCondition Condition (scalarValueCondition2) Failed: ResultSet 1 Row 1 Column 1: values do not match, actual '-100' expected '100'. このエラーは、ストアド プロシージャの定義に軽微なエラーが含まれているために発生します。

    次に、エラーを修正し、テストを再実行します。

Sales.uspFillOrder のエラーを修正する

  1. SQL Server オブジェクト エクスプローラーのデータベースの [プロジェクト] ノードで uspFillOrder ストアド プロシージャをダブルクリックし、Transact-SQL エディターで定義を開きます。

  2. 定義内で、次の Transact-SQL ステートメントを探します。

    UPDATE [Sales].[Customer]
        SET YTDSales = YTDSales - @Delta
    WHERE [CustomerID] = @CustomerID;
    
  3. ステートメントの SET 句を次のステートメントに一致するように変更します。

    UPDATE [Sales].[Customer]
        SET YTDSales = YTDSales + @Delta
    WHERE [CustomerID] = @CustomerID;
    
  4. [ ファイル ] メニューの [ uspFillOrder.sql保存] を選択します。

  5. テスト ビューで、Sales_uspFillOrderTestを右クリックし、[選択項目の実行] を選択します。

    テストは成功します。

ネガティブ単体テストを追加する

ネガティブ テストを作成し、テストがエラーになる必要があるときにエラーになるかどうかを検証する場合があります。 たとえば、既に入力された注文を取り消そうとした場合、そのテストはエラーになる必要があります。 チュートリアルのこの部分では、Sales.uspCancelOrder ストアド プロシージャのネガティブ単体テストを作成します。

ネガティブ テストを作成して検証するには、次のタスクを実行する必要があります。

  • エラー条件をテストするようにストアド プロシージャを更新する

  • 新しい単体テストを定義する

  • 予期されたエラーであることを示すように単体テストのコードを変更する

  • 単体テストを実行する

ストアド プロシージャを更新する

  1. データベースの SimpleUnitTestDB プロジェクト ノードで、[プログラミング] ノードを展開し、[ストアド プロシージャ] ノードを展開して、uspCancelOrderをダブルクリックします。

  2. Transact-SQL エディターで、プロシージャの定義を次のコードに一致するように更新します。

    CREATE PROCEDURE [Sales].[uspCancelOrder]
    @OrderID INT
    AS
    BEGIN
        DECLARE @Delta AS INT, @CustomerID AS INT, @PriorStatus AS CHAR (1);
        BEGIN TRANSACTION;
        BEGIN TRY
            IF (NOT EXISTS (SELECT [CustomerID]
                            FROM [Sales].[Orders]
                            WHERE [OrderID] = @OrderID))
                BEGIN
    -- Specify WITH LOG option so that the error is
    -- written to the application log.
                    RAISERROR ('That order does not exist.',
    -- Message text   16,
    -- severity   1
    -- state
    )
                        WITH LOG;
                END
            SELECT @Delta = [Amount],
                   @CustomerID = [CustomerID],
                   @PriorStatus = [Status]
            FROM [Sales].[Orders]
            WHERE [OrderID] = @OrderID;
            IF @PriorStatus <> 'O'
                BEGIN
    -- Specify WITH LOG option so that the error is
    -- written to the application log.
                    RAISERROR ('You can only cancel open orders.',
    -- Message text   16,
    -- Severity   1
    -- State
    )
                        WITH LOG;
                END
            ELSE
                BEGIN
    -- If we make it to here, then we can cancel the order. Update the status to 'X' first...
                    UPDATE [Sales].[Orders]
                        SET [Status] = 'X'
                    WHERE [OrderID] = @OrderID
    -- and then remove the amount from the YTDOrders for the customer
    ;
                    UPDATE [Sales].[Customer]
                        SET YTDOrders = YTDOrders - @Delta
                    WHERE [CustomerID] = @CustomerID;
                    COMMIT TRANSACTION;
                    RETURN 1;
    -- indicate success
                END
        END TRY
        BEGIN CATCH
            DECLARE @ErrorMessage AS NVARCHAR (4000);
            DECLARE @ErrorSeverity AS INT;
            DECLARE @ErrorState AS INT;
            SELECT @ErrorMessage = ERROR_MESSAGE(),
                   @ErrorSeverity = ERROR_SEVERITY(),
                   @ErrorState = ERROR_STATE();
            ROLLBACK;
            RAISERROR (@ErrorMessage, @ErrorSeverity, @ErrorState);
            RETURN 0;
        END CATCH
    END
    
    -- Use RAISERROR inside the CATCH block to return  -- error information about the original error that  -- caused execution to jump to the CATCH block.  -- Message text  -- Severity  -- State  -- indicate failure
    
  3. [ ファイル ] メニューの [ uspCancelOrder.sql保存] を選択します。

  4. F5 キーを押してSimpleUnitTestDBをデプロイします。

    uspCancelOrder ストアド プロシージャに更新プログラムをデプロイします。 その他のオブジェクトは変更していないため、そのストアド プロシージャのみが更新されます。

    次に、このプロシージャの関連付けられた単体テストを定義します。

uspCancelOrder の SQL Server 単体テストを記述する

  1. SQL Server 単体テスト デザイナーのナビゲーション バーで 、Sales_uspCancelOrderTestを選択し、隣接する一覧で テスト が強調表示されていることを確認します。

    この手順を実行したら、単体テスト内のテスト アクション用のテスト スクリプトを作成できます。

  2. Transact-SQL エディターで、次のステートメントに一致するように Transact-SQL ステートメントを更新します。

    -- ssNoVersion unit test for Sales.uspFillOrder
    DECLARE @RC AS INT, @CustomerID AS INT, @Amount AS INT, @FilledDate AS DATETIME, @Status AS CHAR (1);
    
    DECLARE @CustomerName AS NVARCHAR (40), @OrderID AS INT;
    
    SELECT @RC = 0,
           @CustomerID = 0,
           @OrderID = 0,
           @CustomerName = N'Fictitious Customer',
           @Amount = 100,
           @FilledDate = getdate(),
           @Status = 'O';
    -- NOTE: Assumes that you inserted a Customer record with CustomerName='Fictitious Customer' in the pre-test script.
    
    SELECT @CustomerID = [CustomerID]
    FROM [Sales].[Customer]
    WHERE [CustomerName] = @CustomerName;
    -- Get the most recently added order.
    
    SELECT @OrderID = MAX([OrderID])
    FROM [Sales].[Orders]
    WHERE [CustomerID] = @CustomerID;
    -- try to cancel an order for that customer that has already been filled
    
    EXECUTE
        @RC = [Sales].[uspCancelOrder]
        @OrderID;
    
    SELECT @RC AS RC;
    
  3. [ テスト条件 ] ウィンドウで、[不確定テスト条件] を選択し、[ テスト条件の削除 ] アイコンを選択します。

  4. [ テスト条件 ] ウィンドウで、一覧で [スカラー値 ] を選択し、[ テスト条件の追加] アイコンを選択します。

  5. [プロパティ] ウィンドウで、 [予期される値] プロパティを 0 に設定します。

  6. SQL Server 単体テスト デザイナーのナビゲーション バーで 、Sales_uspCancelOrderTestを選択し、隣接する一覧で [事前テスト ] が強調表示されていることを確認します。 この手順の実行後に、テストの実行に必要な状態にデータを変更するステートメントを指定できます。 この例では、注文を行う前に、 Customer レコードを作成する必要があります。

  7. [ ここをクリック] を選択して作成 し、事前テスト スクリプトを作成します。

  8. Transact-SQL エディターで、次のステートメントに一致するように Transact-SQL ステートメントを更新します。

    /*
                                  Add Transact-SQL statements here to run before the test script is run.
                                  */
    BEGIN TRANSACTION
    -- Add a customer for this test with the name 'CustomerB'
    ;
    
    DECLARE @NewCustomerID AS INT, @RC AS INT, @CustomerName AS NVARCHAR (40);
    
    SELECT @RC = 0,
           @NewCustomerID = 0,
           @CustomerName = N'Fictitious Customer';
    
    IF NOT EXISTS (SELECT *
                   FROM [Sales].[Customer]
                   WHERE CustomerName = @CustomerName)
        BEGIN
                    EXECUTE
                @NewCustomerID = [Sales].[uspNewCustomer]
                @CustomerName;
        END
    
    DECLARE @CustomerID AS INT, @Amount AS INT, @OrderDate AS DATETIME, @FilledDate AS DATETIME, @Status AS CHAR (1), @OrderID AS INT;
    
    SELECT @RC = 0,
           @CustomerID = 0,
           @OrderID = 0,
           @CustomerName = N'Fictitious Customer',
           @Amount = 100,
           @OrderDate = getdate(),
           @FilledDate = getdate(),
           @Status = 'O';
    -- NOTE: Assumes that you inserted a Customer record with CustomerName='Fictitious Customer' in the pre-test script.
    
    SELECT @CustomerID = [CustomerID]
    FROM [Sales].[Customer]
    WHERE [CustomerName] = @CustomerName;
    -- delete any old records in the Orders table and clear out the YTD Sales/Orders fields
    
    DELETE [Sales].[Orders]
    WHERE [CustomerID] = @CustomerID;
    
    UPDATE [Sales].[Customer]
        SET YTDOrders = 0,
            YTDSales  = 0
    WHERE [CustomerID] = @CustomerID;
    
    EXECUTE
        @OrderID = [Sales].[uspPlaceNewOrder]
        @CustomerID,
        @Amount,
        @OrderDate,
        @Status;
    
    EXECUTE
        @RC = [Sales].[uspFillOrder]
        @OrderID,
        @FilledDate;
    
    COMMIT TRANSACTION;
    
    -- place an order for that customer  -- fill the order for that customer
    
  9. [ファイル] メニューの [すべてを保存] をクリックします。

    この時点で、テストを実行する準備ができました。

SQL Server 単体テストを実行する

  1. テスト ビューでSales_uspCancelOrderTestを右クリックし、[選択項目の実行] を選択します。

  2. [テスト結果] ウィンドウの結果を確認します。

    テストは失敗し、次のエラーが表示されます。

    Test method TestProject1.SqlServerUnitTests1.Sales_uspCancelOrderTest threw exception: System.Data.SqlClient.SqlException: You can only cancel open orders.
    

    次に、例外が予期されたものであることを示すようにコードを変更します。

単体テストのコードを変更する

  1. ソリューション エクスプローラーでTestProject1 を展開し、SqlServerUnitTests1.csを右クリックし、[コードの表示] を選択します。

  2. コード エディターで、Sales_uspCancelOrderTest メソッドに移動します。 メソッドの属性を次のコードのように変更します。

    [TestMethod(), ExpectedSqlException(Severity=16, MatchFirstError=false, State=1)]
    public void Sales_uspCancelOrderTest()
    

    特定の例外が表示されることを予期するよう指定します。 必要に応じて、特定のエラー番号を指定することもできます。 この属性を追加しない場合、単体テストは失敗し、[テスト結果] ウィンドウにメッセージが表示されます

  3. [ファイル] メニューの [SqlServerUnitTests1.cs保存] を選択します。

    次に、単体テストを再実行して、期待どおりに失敗することを確認します。

SQL Server 単体テストを再実行する

  1. テスト ビューでSales_uspCancelOrderTestを右クリックし、[選択項目の実行] を選択します。

  2. [テスト結果] ウィンドウの結果を確認します。

    テストが成功したということは、プロシージャが失敗すると想定されていた場合に失敗したことを意味します。