チュートリアル : データベース単体テストの作成と実行
このチュートリアルでは、いくつかのストアド プロシージャの動作を検証するデータベース単体テストを作成します。 データベース単体テストを作成すると、アプリケーションの不適切な動作の原因となる可能性があるコードの障害の特定に役立ちます。 データベース単体テストとアプリケーション テストは、自動化されたテスト スイートの一部として実行できます。
このチュートリアルでは次のタスクを行います。
データベース スキーマを含むスクリプトの作成
データベース プロジェクトの作成とそのスキーマのインポート
データベース プロジェクトの分離開発環境への配置
データベース単体テストの作成
テスト ロジックの定義
データベース単体テストの実行
否定的単体テストの追加
単体テストのいずれかがストアド プロシージャのエラーを検出したら、そのエラーを修正してテストを再実行します。
必須コンポーネント
このチュートリアルを完了するには、データベースを作成および配置するためのアクセス許可があるデータベース サーバーに接続できる必要があります。 詳細については、「Visual Studio のデータベース機能に必要なアクセス許可」を参照してください。
データベース スキーマを含むスクリプトの作成
スキーマのインポートに使用するスクリプトを作成するには
[ファイル] メニューの [新規作成] をポイントし、[ファイル] をクリックします。
[新しいファイル] ダイアログ ボックスが表示されます。
[カテゴリ] ボックスの一覧で、[全般] がまだ強調表示されていない場合は、クリックします。
[テンプレート] ボックスの一覧の [SQL ファイル] をクリックし、[開く] をクリックします。
Transact-SQL エディターが表示されます。
次の 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/2020')); 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/2020')); GO PRINT N'Creating Sales.uspCancelOrder...'; GO CREATE PROCEDURE [Sales].[uspCancelOrder] @OrderID INT AS BEGIN DECLARE @Delta INT, @CustomerID 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 INT, @CustomerID 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); SELECT 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 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
[ファイル] メニューの [名前を付けて SqlQuery_1.sql を保存] をクリックします。
[名前を付けてファイルを保存] ダイアログ ボックスが表示されます。
[オブジェクト名] に「SampleImportScript.sql」と入力します。
ファイルはコンピューター上の任意の場所に保存できます。 場所のメモを作成し、このスクリプトを次のプロシージャで使用できるようにします。
[保存] をクリックします。
[ファイル] メニューの [ソリューションを閉じる] をクリックします。
次に、データベース プロジェクトを作成し、作成したスクリプトからスキーマをインポートします。
データベース プロジェクトの作成とそのスキーマのインポート
データベース プロジェクトを作成するには
[ファイル] メニューの [新規作成] をポイントし、[プロジェクト] をクリックします。
[新しいプロジェクト] ダイアログ ボックスが表示されます。
[インストールされたテンプレート] で、[データベース] ノードを展開し、[SQL Server] をクリックします。
注意
Visual Studio Professional を使用している場合は、代わりに [インストールされたテンプレート] で、[データベース] ノードを展開し、[SQL Server] ノードを展開します。次に、[詳細設定] をクリックします。
テンプレートの一覧の [SQL Server 2008 データベース プロジェクト] をクリックします。
注意
データベースを別のデータベース バージョンに配置する場合は、代わりに対象サーバーに対応するテンプレートを選択してください。
[名前] ボックスに「SimpleUnitTestDB」と入力します。
[ソリューションのディレクトリを作成] チェック ボックスがまだオンになっていない場合はオンにします。
[ソース管理に追加] チェック ボックスがまだオフになっていない場合はオフにし、[OK] をクリックします。
データベース プロジェクトが作成され、ソリューション エクスプローラーに表示されます。 次に、スクリプトからデータベース スキーマをインポートします。
スクリプトからデータベース スキーマをインポートするには
[プロジェクト] メニューの [スクリプトのインポート] をクリックします。
[ようこそ] ページを読んでから、[次へ] をクリックします。
[参照] をクリックし、SampleImportScript.sql ファイルを保存した場所のパスを指定します。
[SampleImportScript.sql] ファイルをダブルクリックし、[完了] をクリックします。
指定したスクリプトがインポートされ、このスクリプトで定義したオブジェクトがデータベース プロジェクトに追加されます。
概要を確認し、[完了] をクリックして操作を完了します。
注意
Sales.uspFillOrder プロシージャには、内部的なコーディング エラーがあります。このエラーを後の手順で見つけて修正します。
作成されたプロジェクトを確認するには
ソリューション エクスプローラーで、[スキーマ オブジェクト] 子ノードを展開します。
階層内で [スキーマ オブジェクト] ノードの下にある下位ノードを調べます。
ソリューション エクスプローラーには、データベース オブジェクトを定義するファイルがあります。
[表示] メニューの [データベース スキーマ ビュー] をクリックします。
スキーマ ビューで、[SimpleUnitTestDB] ノードを展開します。
階層内で [SimpleUnitTestDB] ノードの下にある下位ノードを調べます。
スキーマ ビューには、ソリューション エクスプローラーに表示されるファイルで定義されているオブジェクトが表示されます。
分離開発環境の配置
次に、インポートしたスキーマがあってデータはないデータベースを作成するため、プロジェクトを配置します。 このデータベースは分離開発環境 (サンドボックス) に作成するため、他の作業の干渉を受けることなく、開発およびテストを行うことができます。
データベース プロジェクトを構成およびビルドするには
ソリューション エクスプローラーで、[SimpleUnitTestDB] データベース プロジェクトをクリックします。
[プロジェクト] メニューの [SimpleUnitTestDB のプロパティ] をクリックします。
プロジェクトのプロパティ ダイアログ ボックスが表示されます。
[配置] タブをクリックします。
[配置設定の構成] ボックスの一覧の [マイ分離開発環境] をクリックします。 分離開発環境用の設定を構成することで、ステージング サーバーまたは運用サーバーなどの他の環境で使用する設定とは異なる配置設定を使用できます。
[配置動作] ボックスの一覧の [配置スクリプト (.sql) を作成してデータベースに配置します] をクリックします。
[ターゲット データベースの設定] の [編集] をクリックします。
[接続のプロパティ] ダイアログ ボックスが表示されます。
作成するデータベースの接続プロパティを設定し、[OK] をクリックします。
[ターゲット接続] ボックスに正しい接続文字列が表示されます。
ヒント
データベースは、テスト サーバー、開発サーバー、またはローカル コンピューターに作成する必要があります。 運用サーバーは指定しないでください。
[ターゲット データベース名] に「SimpleUnitTestDB」と入力します。
[配置構成ファイル] の横の [編集] をクリックします。
[データ損失が発生する場合に増分配置をブロック] チェック ボックスをオフにします。
注意
このチュートリアルでは、データベース単体テストの一部として配置した空のデータベースに対して、ストアド プロシージャをテストします。 分離開発環境でストアド プロシージャをテストするため、既存のデータを保持しておく必要はありません。
[ファイル] メニューの [すべてを保存] をクリックします。
[ビルド] メニューの [ソリューションのビルド] をクリックします。
先ほど設定したプロパティに基づいて、配置スクリプトのビルド方法が決まります。 ビルドの状態が [出力] ウィンドウに表示され、最後の行に "ビルド: 1 正常終了または最新の状態" と表示されます。
注意
出力ウィンドウが表示されない場合は、[表示] メニューの [出力] をクリックします。
データベース プロジェクトを配置するには
ソリューション エクスプローラーで、[SimpleUnitTestDB] データベース プロジェクトをクリックします。
[ビルド] メニューの [SimpleUnitTestDB の配置] をクリックします。
ヒント
この配置は、テスト サーバー、開発サーバー、またはローカル コンピューターに対して実行する必要があります。 運用サーバーは指定しないでください。
データベース プロジェクトが新しいデータベースに配置されます。 配置の状態が出力ウィンドウに表示され、最後の行に "配置: 1 正常終了" と表示されます。 テスト データをデータベース内に作成するためのデータ生成計画を定義することもできます。 このチュートリアルでは、データベースにデータを生成する必要のない、簡単なデータベースをテストします。
データベース単体テストの作成
ストアド プロシージャのデータベース単体テストを作成するには
[表示] メニューの [スキーマ ビュー] をクリックします。
スキーマ ビューで、[スキーマ] ノードを展開し、[Sales] ノードを展開し、[プログラミング] ノードを展開し、[ストアド プロシージャ] ノードを展開します。
[uspNewCustomer] ストアド プロシージャを右クリックし、[単体テストの作成] をクリックします。
[単体テストの作成] ダイアログ ボックスが表示されます。
[Sales.uspCancelOrder]、[Sales.uspFillOrder]、[Sales.uspNewCustomer]、[Sales.uspPlaceNewOrder]、および [Sales.uspShowOrderDetails] の 5 つのストアド プロシージャすべてのチェック ボックスをオンにします。
[プロジェクト] の [新しい Visual C# テスト プロジェクトの作成] をクリックします。
プロジェクト名およびクラス名として既定の名前を受け入れ、[OK] をクリックします。
[プロジェクト 'TestProject1' の構成] ダイアログ ボックスが表示されます。
[次のデータ接続を使用して単体テストを実行] で、このチュートリアルで以前に配置したデータベースへの接続を指定します。
注意
アクセス許可が制限されたビューまたはストアド プロシージャをテストする必要がある場合、通常この手順の中でその接続を指定します。 次に、テストを検証するため、より広い範囲のアクセス許可を与えて 2 番目の接続を指定します。 2 番目の接続がある場合、そのユーザーをデータベース プロジェクトに追加し、そのユーザーのログインを配置前スクリプトに作成する必要があります。
[配置] の [単体テストの実行前に自動的にデータベース プロジェクトを配置] チェック ボックスをオンにします。
[データベース プロジェクト] の [SimpleUnitTestDB.dbproj] をクリックします。
[配置構成] の [デバッグ] をクリックします。
テスト データをデータベース単体テストの一部として生成することもできます。 このチュートリアルでは、テスト自体のデータがテストで作成されるため、その手順は省略します。
[OK] をクリックします。
テスト プロジェクトがビルドされ、データベース単体テスト デザイナーが表示されます。 次に、単体テストの Transact-SQL スクリプトのテスト ロジックを更新します。
テスト ロジックの定義
この単純なデータベースには、Customer と Order の 2 つのテーブルがあります。 次のストアド プロシージャを使用してデータベースを更新します。
uspNewCustomer - このストアド プロシージャは、Customer テーブルにレコードを追加します。ここで、顧客の YTDOrders 列と YTDSales 列が 0 に設定されます。
uspPlaceNewOrder - このストアド プロシージャは、指定した顧客のレコードを Orders テーブルに追加し、Customer テーブル内の対応するレコードの YTDOrders の値を更新します。
uspFillOrder - このストアド プロシージャは、'O' ステータスを 'F' ステータスに変更することで、Orders テーブル内のレコードを更新し、Customer テーブル内の対応するレコードの YTDSales の金額を加算します。
uspCancelOrder - このストアド プロシージャは、'O' ステータスを 'X' ステータスに変更することで、Orders テーブル内のレコードを更新し、Customer テーブル内の対応するレコードの YTDOrders の金額を減算します。
uspShowOrderDetails - このストアド プロシージャは、Orders テーブルを Custom テーブルと結合し、特定の顧客のレコードを表示します。
注意
この例では、単純なデータベース単体テストの作成方法を示します。 実務に使用するデータベースでは、特定の顧客の 'O' ステータスまたは 'F' ステータスのすべての注文の合計金額を集計することがあります。 また、このチュートリアルの手順には、エラー処理も含まれていません。 たとえば、既に入力された注文に対し、uspFillOrder の呼び出しを防ぐ処理がされません。
このテストでは、データベースはデータがない状態で開始されるものとして扱われます。 これから、次の条件を検証するテストを作成します。
uspNewCustomer - このストアド プロシージャの実行後、Customer テーブルに 1 行のレコードが含まれていることを確認します。
uspPlaceNewOrder - CustomerID が 1 の顧客に対し、100 ドルの注文を追加します。 その顧客の YTDOrders の金額が 100 であり、YTDSales の金額が 0 であることを確認します。
uspFillOrder - CustomerID が 1 の顧客に対し、50 ドルの注文を追加します。 その注文を入力します。 YTDOrders と YTDSales の両方の金額が 50 であることを確認します。
uspShowOrderDetails - CustomerID が 1 の顧客に対し、100 ドル、50 ドル、および 5 ドルの注文を追加します。 uspShowOrderDetails で適切な列数が返され、結果セットに予想どおりのチェックサムがあることを確認します。
注意
一般的な手順では、データベース単体テスト全体のセットに対し、他の列が適切に設定されていることを確認します。 このチュートリアルを扱いやすい長さに留めるため、uspCancelOrder の動作の検証方法は説明しません。
uspNewCustomer のデータベース単体テストを作成するには
データベース単体テスト デザイナーのナビゲーション バーで、[Sales_uspNewCustomerTest] をクリックし、[テスト] が隣接するリストで強調表示されていることを確認します。
前の手順の実行後、単体テスト内のテスト アクションのテスト スクリプトを作成できます。
Transact-SQL エディターで、Transact-SQL ステートメントを次のステートメントのように変更します。
-- database 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];
テスト条件ペインで、[結果不確定] のテスト条件をクリックし、[テスト条件を削除します] (x) をクリックします。
テスト条件ペインの一覧で、[行数] をクリックし、[テスト条件を追加します] (+) をクリックします。
[プロパティ] ウィンドウで、[行数] プロパティを 1 に設定します。
[ファイル] メニューの [すべてを保存] をクリックします。
次に、uspPlaceNewOrder の単体テストのロジックを定義します。
uspPlaceNewOrder のデータベース単体テストを作成するには
データベース単体テスト デザイナーのナビゲーション バーで、[Sales_uspPlaceNewOrderTest] をクリックし、[テスト] が隣接するリストで強調表示されていることを確認します。
この手順の実行後、単体テスト内のテスト アクションのテスト スクリプトを作成できます。
Transact-SQL エディターで、Transact-SQL ステートメントを次のステートメントのように変更します。
-- database 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); 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; -- 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
テスト条件ペインで、[結果不確定] のテスト条件をクリックし、[テスト条件を削除します] (x) をクリックします。
テスト条件ペインの一覧で、[スカラー値] をクリックし、[テスト条件を追加します] (+) をクリックします。
[プロパティ] ウィンドウで、[予期される値] プロパティを 100 に設定します。
データベース単体テスト デザイナーのナビゲーション バーで、[Sales_uspPlaceNewOrderTest] をクリックし、[テスト前] が隣接するリストで強調表示されていることを確認します。
この手順の実行後、テストの実行のために必要な状態にデータを変更するステートメントを指定できます。 たとえば、Customer レコードは、その顧客の注文を作成する前に、作成されている必要があります。
[作成するにはここをクリックしてください] をクリックして、事前テスト スクリプトを作成します。
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 from [Sales].[Orders] WHERE [CustomerID] = @CustomerID; UPDATE [Sales].[Customer] SET YTDOrders = 0, YTDSales = 0 WHERE [CustomerID] = @CustomerID;
[ファイル] メニューの [すべてを保存] をクリックします。
次に、uspFillOrder の単体テストを作成します。
uspFillOrder のデータベース単体テストを作成するには
データベース単体テスト デザイナーのナビゲーション バーで、[Sales_uspFillOrderTest] をクリックし、[テスト] が隣接するリストで強調表示されていることを確認します。
この手順の実行後、単体テスト内のテスト アクションのテスト スクリプトを作成できます。
Transact-SQL エディターで、Transact-SQL ステートメントを次のステートメントのように変更します。
-- database 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;
テスト条件ペインで、[結果不確定] のテスト条件をクリックし、[テスト条件を削除します] (x) をクリックします。
テスト条件ペインの一覧で、[スカラー値] をクリックし、[テスト条件を追加します] (+) をクリックします。
[プロパティ] ウィンドウで、[予期される値] プロパティを 100 に設定します。
データベース単体テスト デザイナーのナビゲーション バーで、[Sales_uspFillOrderTest] をクリックし、[テスト前] が隣接するリストで強調表示されていることを確認します。 この手順の実行後、テストの実行のために必要な状態にデータを変更するステートメントを指定できます。 たとえば、Customer レコードは、その顧客の注文を作成する前に、作成されている必要があります。
[作成するにはここをクリックしてください] をクリックして、事前テスト スクリプトを作成します。
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 from [Sales].[Orders] WHERE [CustomerID] = @CustomerID; UPDATE [Sales].[Customer] SET YTDOrders = 0, YTDSales = 0 WHERE [CustomerID] = @CustomerID; -- place an order for that customer EXECUTE @RC = [Sales].[uspPlaceNewOrder] @CustomerID, @Amount, @OrderDate, @Status; COMMIT TRANSACTION
[ファイル] メニューの [すべてを保存] をクリックします。
これでテストを実行する準備が整いました。
uspShowOrderDetails のデータベース単体テストを作成するには
データベース単体テスト デザイナーのナビゲーション バーで、[Sales_uspShowOrderDetailsTest] をクリックし、[テスト] が隣接するリストで強調表示されていることを確認します。
この手順の実行後、単体テスト内のテスト アクションのテスト スクリプトを作成できます。
Transact-SQL エディターで、Transact-SQL ステートメントを次のステートメントのように変更します。
-- database 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;
テスト条件ペインで、[結果不確定] のテスト条件をクリックし、[テスト条件を削除します] (x) をクリックします。
テスト条件ペインの一覧で、[必要なスキーマ] をクリックし、[テスト条件を追加します] (+) をクリックします。
[プロパティ] ウィンドウの [構成] プロパティで、参照ボタン ('[…]') をクリックします。
[expectedSchemaCondition1 の構成] ダイアログ ボックスで、データベースへの接続を指定します。
[取得] をクリックします。
単体テストの Transact-SQL 本文が実行され、結果のスキーマがダイアログ ボックスに表示されます。 事前テスト コードは実行されていないため、データは返されません。 ここではスキーマを検証するだけで、データを検証するのではないため、問題はありません。
[OK] をクリックします。
予期されるスキーマがテスト条件と一緒に格納されます。
データベース単体テスト デザイナーのナビゲーション バーで、[Sales_uspShowOrderDetailsTest] をクリックし、[テスト前] が隣接するリストで強調表示されていることを確認します。 この手順の実行後、テストの実行のために必要な状態にデータを変更するステートメントを指定できます。 たとえば、Customer レコードは、その顧客の注文を作成する前に、作成されている必要があります。
[作成するにはここをクリックしてください] をクリックして、事前テスト スクリプトを作成します。
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 '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 from [Sales].[Orders] WHERE [CustomerID] = @CustomerID; UPDATE [Sales].[Customer] SET YTDOrders = 0, YTDSales = 0 WHERE [CustomerID] = @CustomerID; -- place 3 orders for that customer 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
データベース単体テスト デザイナーのナビゲーション バーで、[Sales_uspShowOrderDetailsTest] をクリックし、隣接するリストで [テスト] をクリックします。
チェックサム条件を事前テストでなくテストに適用するため、この操作を実行する必要があります。
テスト条件ペインの一覧で、[データ チェックサム] をクリックし、[テスト条件を追加します] (+) をクリックします。
[プロパティ] ウィンドウの [構成] プロパティで、参照ボタン ('[…]') をクリックします。
[checksumCondition1 の構成] ダイアログ ボックスで、データベースへの接続を指定します。
ダイアログ ボックスで 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 from [Sales].[Orders] WHERE [CustomerID] = @CustomerID; UPDATE [Sales].[Customer] SET YTDOrders = 0, YTDSales = 0 WHERE [CustomerID] = @CustomerID; -- place 3 orders for that customer 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 -- database unit test for Sales.uspFillOrder 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'; -- 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;
このコードは、事前テストからの Transact-SQL コードを、テスト自体からの Transact-SQL と組み合わせたものです。 これらの両方が、テストの実行時に返される結果と、同じ結果を返すために必要です。
[取得] をクリックします。
指定した Transact-SQL が実行され、返されたデータのチェックサムが計算されます。
[OK] をクリックします。
計算されたチェックサムがテスト条件と一緒に格納されます。 データ チェックサム テスト条件の [値] 列には、予期されるチェックサムの値が表示されます。
[ファイル] メニューの [すべてを保存] をクリックします。
これでテストを実行する準備が整いました。
データベースの単体テストを実行する
データベース単体テストを実行するには
[テスト] メニューの [ウィンドウ] をポイントし、[テスト ビュー] をクリックします。
[テスト ビュー] ウィンドウで、ツール バーの [最新の情報に更新] をクリックし、テストの一覧を更新します。
[テスト ビュー] ウィンドウに、このチュートリアルの前の手順で作成したテストと、そのテストに追加した Transact-SQL ステートメントおよびテスト条件の一覧が表示されます。 TestMethod1 という名前のテストは空で、このチュートリアルでは使用しません。
[Sales_uspNewCustomerTest] を右クリックし、[選択範囲の実行] をクリックします。
Visual Studio は、データベースへの接続のために指定した特権コンテキストを使用し、データ生成計画を適用します。 次に、Visual Studio はテスト内の Transact-SQL スクリプトを実行する前に、実行コンテキストに切り替えます。 最後に、Visual Studio は Transact-SQL スクリプトの結果を、テスト条件に指定された内容と照合して評価し、合格か不合格かの結果を [テスト結果] ウィンドウに表示します。
[テスト結果] ウィンドウの結果を確認します。
テストに合格するのは、SELECT ステートメントを実行して 1 行が返される場合です。
Sales_uspPlaceNewOrderTest、Sales_uspFillOrderTest、および Sales_uspShowOrderDetailsTest の各テストについて、手順 3. を繰り返します。 結果は次のようになります。
テスト
期待される結果
Sales_uspPlaceNewOrderTest
成功
Sales_uspShowOrderDetailsTest
成功
Sales_uspFillOrderTest
"ScalarValueCondition 条件 (scalarValueCondition2) が失敗しました: ResultSet 1 行 1 列 1: 値が一致しません。実際は '-100' ですが、指定は '100' です" というエラーが表示されて失敗します。 このエラーは、ストアド プロシージャの定義に重大でないエラーが含まれているために発生します。
次に、エラーを修正してテストを再実行します。
Sales.uspFillOrder のエラーを修正するには
スキーマ ビューで、[uspFillOrder] ストアド プロシージャをダブルクリックし、この定義を Transact-SQL エディターで開きます。
定義から次の Transact-SQL ステートメントを探します。
UPDATE [Sales].[Customer] SET YTDSales = YTDSales - @Delta WHERE [CustomerID] = @CustomerID
このステートメントの SET 句を次のステートメントのように変更します。
UPDATE [Sales].[Customer] SET YTDSales = YTDSales + @Delta WHERE [CustomerID] = @CustomerID
[ファイル] メニューの [uspFillOrder.proc.sql の保存] をクリックします。
[テスト ビュー] ウィンドウで [Sales_uspFillOrderTest] を右クリックし、[選択範囲の実行] をクリックします。
テストに合格します。
否定的単体テストの追加
否定的テストを作成し、テストがエラーになるべき場合にエラーになるかどうかを確認することができます。 たとえば、既に入力された注文を取り消そうとした場合、そのテストはエラーになる必要があります。 このチュートリアルのこの部分では、Sales.uspCancelOrder ストアド プロシージャのための否定的単体テストを作成します。
否定的テストを作成して検証するには、次のタスクを実行する必要があります。
エラー条件のテストのためのストアド プロシージャの更新
新しい単体テストの定義
予期されるエラーを示すための単体テストのコードの変更
単体テストの実行
ストアド プロシージャを更新するには
スキーマ ビューで、[SimpleUnitTestDB] ノード、[スキーマ] ノード、[販売] ノード、[プログラミング] ノード、[ストアド プロシージャ] ノードの順に展開し、[uspCancelOrder] をダブルクリックします。
Transact-SQL エディターで、プロシージャの定義を次のコードのように変更します。
CREATE PROCEDURE [Sales].[uspCancelOrder] @OrderID INT AS BEGIN DECLARE @Delta INT, @CustomerID INT, @PriorStatus 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 NVARCHAR(4000); DECLARE @ErrorSeverity INT; DECLARE @ErrorState INT; SELECT @ErrorMessage = ERROR_MESSAGE(), @ErrorSeverity = ERROR_SEVERITY(), @ErrorState = ERROR_STATE(); ROLLBACK TRANSACTION -- Use RAISERROR inside the CATCH block to return -- error information about the original error that -- caused execution to jump to the CATCH block. RAISERROR (@ErrorMessage, -- Mesasge text @ErrorSeverity, -- Severity @ErrorState -- State ); RETURN 0; -- indicate failure END CATCH; END
[ファイル] メニューの [uspCancelOrder.proc.sql の保存] をクリックします。
ソリューション エクスプローラーで、[SimpleUnitTestDB] を右クリックし、[配置] をクリックします。
uspCancelOrder ストアド プロシージャへの更新を配置します。 その他のオブジェクトは変更していないため、そのストアド プロシージャのみが更新されます。
次に、このプロシージャに関連付けられた単体テストを定義します。
uspCancelOrder のデータベース単体テストを作成するには
データベース単体テスト デザイナーのナビゲーション バーで、[Sales_uspCancelOrderTest] をクリックし、[テスト] が隣接するリストで強調表示されていることを確認します。
この手順の実行後、単体テスト内のテスト アクションのテスト スクリプトを作成できます。
Transact-SQL エディターで、Transact-SQL ステートメントを次のステートメントのように変更します。
-- database 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;
テスト条件ペインで、[結果不確定] のテスト条件をクリックし、[テスト条件を削除します] (x) をクリックします。
テスト条件ペインの一覧で、[スカラー値] をクリックし、[テスト条件を追加します] (+) をクリックします。
[プロパティ] ウィンドウで、[予期される値] プロパティを 0 に設定します。
データベース単体テスト デザイナーのナビゲーション バーで、[Sales_uspCancelOrderTest] をクリックし、[テスト前] が隣接するリストで強調表示されていることを確認します。 この手順の実行後、テストの実行のために必要な状態にデータを変更するステートメントを指定できます。 たとえば、Customer レコードは、その顧客の注文を作成する前に、作成されている必要があります。
[作成するにはここをクリックしてください] をクリックして、事前テスト スクリプトを作成します。
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, @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 from [Sales].[Orders] WHERE [CustomerID] = @CustomerID; UPDATE [Sales].[Customer] SET YTDOrders = 0, YTDSales = 0 WHERE [CustomerID] = @CustomerID; -- place an order for that customer EXECUTE @OrderID = [Sales].[uspPlaceNewOrder] @CustomerID, @Amount, @OrderDate, @Status; -- fill the order for that customer EXECUTE @RC = [Sales].[uspFillOrder] @OrderID, @FilledDate; COMMIT TRANSACTION
[ファイル] メニューの [すべてを保存] をクリックします。
これでテストを実行する準備が整いました。
データベース単体テストを実行するには
[テスト ビュー] で [Sales_uspCancelOrderTest] を右クリックし、[選択範囲の実行] をクリックします。
[テスト結果] ウィンドウの結果を確認します。
テストが不合格になり、次のエラーが表示されます。
Test method TestProject1.DatabaseUnitTests1.Sales_uspCancelOrderTest threw exception: System.Data.SqlClient.SqlException: You can only cancel open orders.
次に、例外が予期されることを示すように、コードを変更します。
単体テストのコードを変更するには
ソリューション エクスプローラーで [TestProject1] を展開し、[DatabaseUnitTests1.cs] を右クリックし、[コードの表示] をクリックします。
コード エディターで、Sales_uspCancelOrderTest メソッドに移動します。 このメソッドの属性を次のコードのように変更します。
[TestMethod(), ExpectedSqlException(Severity=16, MatchFirstError=false, State=1)] public void Sales_uspCancelOrderTest()
特定の SQL 例外を示すことを予期するように指定します。 オプションとして、特定のエラー番号を指定することもできます。 この属性を追加しないと、単体テストはエラーになり、メッセージが [テスト結果] ウィンドウに表示されます。
[ファイル] メニューの [DatabaseUnitTests1.cs の保存] をクリックします。
次に、単体テストを再実行して、予期したとおりにエラーになることを検証します。
データベース単体テストを再実行するには
[テスト ビュー] で [Sales_uspCancelOrderTest] を右クリックし、[選択範囲の実行] をクリックします。
[テスト結果] ウィンドウの結果を確認します。
テストに合格するのは、プロシージャがエラーになると考えられるときにエラーになった場合です。
次の手順
一般的なプロジェクトでは、追加の単体テストを定義して、重要なデータベース オブジェクトがすべて正常に動作することを確認します。 一連のテストが完了したら、チームで共有できるようにバージョン管理に入れられたテスト結果を調べます。
ベースラインの構築後、データベース オブジェクトの作成や変更を行い、変更内容が予期される動作を妨げていないかどうかを確認する関連テストを作成できます。