MSDN ライブラリ

Azure ソリューションの効率的なテストに関するガイダンス

更新日: 2015年1月

執筆者: Suren Machiraju

校閲者: Jaime Alva Bravo、Steve Wilkins

Microsoft Azure ソリューションの設計、コーディング、およびデプロイを完了してから、ソリューションが動作しないと判明する場合があります。この記事では、ソフトウェア デプロイメントのライフサイクルで Microsoft Azure アプリケーションをテストする方法について説明します。テスト範囲には、ビジネス ロジックと総合的なエンドツーエンド シナリオのテストも含まれています。この記事では、次の方法について説明します。

  • Microsoft Azure コンポーネントへの依存関係を排除しながら、ビジネス ロジック コンポーネントの単体テストを作成します。

  • 統合エンドツーエンド テストを設計します。

  • テストを実行するたびに Microsoft Azure Service リソースのセットアップ、初期化、クリーンアップ、および分解のオーバーヘッドを排除します。

  • ACS 名前空間、Service Bus キューなどのリソースについて、重複するインフラストラクチャの作成を排除します。

また、この記事では、Microsoft Azure アプリケーションをテストする際に役立つ多様なテスト テクノロジと技術の概要についても説明します。

この記事では、次の点が更新されています。

  1. Visual Studio 2013 を使用しています。

  2. Moles は Visual Studio 2013 の Microsoft Fakes で置き換えられました。Fakes については、「Microsoft Fakes を使用したテストでのコードの分離」の記事を参照してください。

  3. Visual Studio 2013 では、新しいバージョンのコードの分離を使用できるようになりました。コードの分離はテスト エクスプローラーから直接呼び出します。説明については、「コード カバレッジを使用した、テストされるプロジェクトのコード割合の確認」の記事を参照してください。

  4. Pex チームは、Code Digger という軽量バージョンの Pex をリリースしました。説明については、「Microsoft Code Digger (英語)」の記事を参照してください。

  5. Visual Studio 2012 以降、プライベート メソッドのテストは推奨していません。説明については、「プライベート メソッドの単体テストに対する私見 (英語)」を参照してください。

テストには 2 つの種類があります。

  • 単体テストは、単一、特定の機能を実行する集中的なテストです。このようなテストのことは CUT (Code Under Test) と呼びます。CUT に必要な依存関係がある場合は、排除する必要があります。

  • 統合テストは、複数の機能を同時に実行する広範なテストです。多くの場合、統合テストは単体テストと共通点がありますが、統合テストでは複数の機能領域を対象とし、複数の依存関係を含みます。

全体的に、テスト ダブルの作成と使用を中心にテストします。ここでは、次の種類のテスト ダブルを使用します。

  • Fakes は、示すオブジェクトと同じインターフェイスを実装するシミュレート済みオブジェクトです。Fakes は定義済みの応答を返します。Fake には複数のメソッド スタブが含まれ、プログラムで構築したものの置き換えとして機能します。

  • スタブはソフトウェア オブジェクトの動作をシミュレートします。

  • shim を使用すると、ソリューションに含まれないコードをアセンブリから分離することができます。これらは、相互にソリューションの分離コンポーネントでもあります。

このようなテストを実行すると、状態と動作を検証することができます。たとえば、メソッドを呼び出し、特定の値が返された場合、状態には結果が含まれています。たとえば、特定の順序で、または特定の回数、メソッドを呼び出す動作があります。

単体テストの主な目標の 1 つは、依存関係を排除することです。Azure フレームワークの場合、次のような依存関係があります。

  • Service Bus キュー

  • Access Control サービス。

  • キャッシュ。

  • Azure テーブル、BLOB、およびキュー。

  • Azure SQL データベース。

  • Azure ドライブ (旧称クラウド ドライブ)。

  • その他の Web サービス。

Azure アプリケーション用のテストを構築する場合、依存関係を置き換え、ロジックの実行に対するテストに集中します。

この記事で説明する Service Bus キューの例 (ツールと技術を含む) は、その他すべての依存関係にも適用されます。

Microsoft Azure アプリケーションのテスト フレームワークを実装するには、以下が必要です。

  • テストを定義し、実行する単体テスト フレームワーク。

  • 依存関係を分離し、限定された範囲の単体テストを構築することができるモック フレームワーク。

  • コード カバレッジを増やし、単体テストの自動生成に役立つツール。

  • 依存関係の挿入を利用し、コントロールの反転 (Inversion of Control Pattern、IoC) を適用するテストが容易な設計に役立つ他のフレームワーク。

Visual Studio には、Visual Studio で作成した単体テストを実行できる、MS Test というコマンドライン ユーティリティが含まれています。また、Visual Studio には、テストをサポートする一連のプロジェクト テンプレートおよび項目テンプレートも含まれています。通常、テスト プロジェクトを作成してから、[TestClass] 属性で修飾したクラス (テスト フィクスチャ) を追加します。このクラスには、[TestMethod] 属性で修飾したメソッドが含まれています。MS Test では、Visual Studio 内の多様なウィンドウを使用して、プロジェクトで定義した単体テストを実行できます。単体テストの実行後に結果を確認することもできます。

noteメモ
Visual Studio 2013 Express、Professional、および Test Professional エディションに MS Test は含まれていません。

MS Test では、単体テストは AAA (Arrange、Act、Assert) パターンに従います。

  • Arrange - 前提条件オブジェクト、構成、その他すべての必要な前提条件を構築し、CUT に必要なものを入力します。

  • Act - コードに対して、実際の限定された範囲のテストを実行します。

  • Assert - 予測した結果になったことを確認します。

MS Test フレームワーク ライブラリには、PrivateObject および PrivateType ヘルパー クラスが含まれています。これらのクラスでは、単体テスト コード内から非パブリック インスタンス メンバーまたは静的メンバーを簡単に呼び出すために、リフレクションを使用します。

Premium および Ultimate エディションの Visual Studio には、強化された単体テスト ツールが含まれています。これらの単体テスト ツールは MS Test と統合されています。また、単体テストが実行するコード量を分析することができます。さらに、範囲を示すためにソース コードを色分けすることもできます。この機能はコード カバレッジと呼ばれます。

単体テストの目標は、分離環境でテストすることです。ただし、多くの場合、分離環境でのテストではコードをテストできません。コードがテスト用に作成されていないこともあります。コードは他のライブラリに依存しており、簡単に分離できないので、コードの書き換えは困難です。たとえば、外部環境とやり取りするコードを簡単に分離することはできません。モック フレームワークを利用すると、両方の種類の依存関係を分離できます。

考えられるモック フレームワークの一覧は、この記事の末尾のリンク セクションを参照してください。この記事では、Microsoft Fakes の使用方法を中心に説明します。

Microsoft Fakes を使用すると、アプリケーションのテスト対象外の部分をスタブや shim と置き換えることで、テスト対象のコードを分離することができます。スタブと shim は、テストの制御下にある短いコードです。テスト対象のコードを分離することで、テストが失敗したときに、原因が他のコードではなくテスト対象のコードにあることがわかります。また、スタブと shim を使用することで、アプリケーションの他の部分がまだ機能していない場合でも、コードをテストできます。

Fakes には 2 つの形式があります。

  • スタブは、同じインターフェイスを実装した小さな代替コードでクラスを置き換えます。スタブを使用するには、各コンポーネントがインターフェイスにのみ依存し、他のコンポーネントに依存していないアプリケーションを設計する必要があります。ここでの "コンポーネント" とは、共に設計および更新され、通常はアセンブリに含まれている 1 つのクラスまたは複数クラスのグループです。

  • shim は、実行時にアプリケーションのコンパイル済みのコードを変更します。アプリケーションでは、指定したメソッドを呼び出す代わりに、テスト用に用意した shim を実行します。shim を使用して、.NET アセンブリなど、変更できないアセンブリの呼び出しを置き換えることができます。

Code Digger を使用して、.NET コード全体の可能性のある実行パスを分析します。結果として、各行に一意のコードの動作が記載されたテーブルが作成されます。このテーブルで、コードの動作を理解することができます。また、隠れているバグを見つけることもできます。

Visual Studio エディターでコードを分析するには、新しいコンテキスト メニュー項目の [入力/出力テーブルの生成] を使用して Code Digger を呼び出します。Code Digger は入力/出力のペアを計算して表示します。Code Digger は、バグ、例外、およびアサーション エラーを体系的に探します。

Code Digger は、既定で、ポータブル クラス ライブラリにあるパブリック .NET コードでのみ動作します。この記事の後半では、Code Digger を構成して他の .NET プロジェクトを確認する方法について説明します。

Code Digger では、Pex エンジンと Microsoft Research の Z3 制約ソルバーを使用して、コードのすべての分岐を体系的に分析します。Code Digger は、高いコード カバレッジを達成するテスト スイートを生成しようとします。

拡張可能な依存関係挿入 (DI) とコントロールの反転 (IoC) コンテナーのために Microsoft Unity を使用できます。傍受、コンストラクター挿入、プロパティ挿入、およびメソッド呼び出し挿入をサポートしています。Microsoft Unity とその他の類似ツールを使用すると、アプリケーションのあらゆるレベルで依存関係を挿入できる、テストが容易な設計を構築できます(ここでは、依存関係挿入とこれらのフレームワークのいずれかを考慮してアプリケーションを設計および構築したという想定しています)。

このようなフレームワークは、テストが容易なコード、最終的に優れたコードを作成するために最適です。また、先行設計要件でも必要になる可能性があります。この記事では、DI および IoC コンテナーについては説明しません。

ここで、Web ロールでホストされている Web サイトを含むソリューションについて説明します。この Web サイトはメッセージをキューにプッシュします。ワーカー ロールはキューのメッセージを処理します。ここでは、これら 3 つすべての側面についてテストします。

発注する Web サイトがあり、Service Bus キューはその注文を処理するためにキューを作成します。この Web ページは図 1 のようになります。

図 1

図 1

ユーザーが [作成] をクリックすると、Service Bus キューは、関連するコントローラーの作成アクションに新しい注文を投稿します。このアクションは次のように実装されます。

noteメモ
MicrosoftAzureQueue クラスは、Service Bus .NET API (MessageSender、MessageReceiver など) を使用して Service Bus キューとやり取りするラッパー クラスです。

private IMicrosoftAzureQueue queue;
public OrderController()
{
    queue = new MicrosoftAzureQueue();
}
public OrderController(IMicrosoftAzureQueue queue)
{
    this.queue = queue;
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "OrderId,Description")] Order order)
{
    try
    {
        if (ModelState.IsValid)
        {
            string connectionString = CloudConfigurationManager.GetSetting("Microsoft.ServiceBus.ConnectionString");
            string queueName = "ProcessingQueue";
            queue.InitializeFromConnectionString(connectionString, queueName);
            queue.Send(order);
        }
        return View("OrderCreated");
    }
    catch (Exception ex)
    {
        Trace.TraceError(ex.Message);
        return View("Error");
    }            
}

このコードは CloudConfigurationManager から構成設定を取得し、注文を含むメッセージをキューに送信します。また、作成アクションは次のメソッドを使用します。

  • InitializeFromConnectionString (ConnectionString string, QueueName string)

  • Send (MicrosoftAzureQueue class)

このようなメソッドの迂回を構築するには、Fakes を使用して動作を制御し、実際の環境の依存関係を排除します。Fakes を使用すると、Azure エミュレーターでテストを実行したり、Service Bus キューを呼び出したりする必要がなくなります。注文の入力がキューに送信されたものであることを確認するために、コントローラーで作成アクションを実行します。Send メソッドでは、アクションの入力として、入力に Order ID と Description があることを確認します。次に結果として OrderCreated ビューが表示されることを確認します。

Fakes を使用して上記の実装の単体テストは、簡単に構築できます。Test プロジェクト内で、まねする種類を含むアセンブリを右クリックします。次に、[Fakes アセンブリに追加] を選択します。

この例では、[Microsoft.ServiceBus] を選択し、[Add Fakes Assembly] を選択します。"Microsoft.ServiceBus.Fakes" という XML ファイルがテスト プロジェクトに追加されます。Microsoft.WindowsAzure.Configuration アセンブリについてアクションを繰り返します。

Test プロジェクトを構築すると、自動生成されたモック バージョンのアセンブリに参照が追加されます。この例で生成されるアセンブリは "Microsoft.ServiceBus.Fakes" と "Microsoft.WindowsAzure.Configuration" です。

単体テスト メソッドを作成し、[TestCategory("With fakes")] 属性を適用します。単体テスト内では、shim を使用して、アプリケーションの各部分を相互に分離します。

単体テストのために shim を使用して他のアセンブリからアプリケーションを分離する

shim は、テスト対象のコンポーネントを環境から簡単に分離するために Microsoft Fakes フレームワークが用意している 2 つのテクノロジの 1 つです。shim は、特定のメソッドの呼び出しを、テストの一環として作成したコードに迂回させます。多くのメソッドは、外部条件に依存してさまざまな結果を返します。一方、shim はテストのコントロール下にあり、いつ呼び出しても一定の結果を返すことができます。そのため、テストの作成が簡単になります。shim を使用すると、ソリューションに含まれないコードをアセンブリから分離することができます。ソリューションのコンポーネントを相互に分離するには、スタブを使用することをお勧めします。

単体テストのためにスタブを使用してアプリケーションの各部分を相互に分離する

スタブは、呼び出す他のコンポーネントからテストしているコンポーネントを簡単に分離するために Microsoft Fakes フレームワークが用意している 2 つのテクノロジの 1 つです。スタブは、テスト中に別のコンポーネントの代替となる小さなコードです。スタブを使用する利点は、一定の結果を返し、テストの作成が簡単なことです。また、他のコンポーネントがまだ動作していない場合でも、テストを実行できます。

このテスト ケースでは、Azure アセンブリの CloudConfigurationManager と BrokeredMessage の shim を使用します。ソリューションのクラスの 1 つである MicrosoftAzureQueue にはスタブを使用します。

[TestMethod]
[TestCategory("With fakes")]
public void Test_Home_CreateOrder()
{
    // Shims can be used only in a ShimsContext
    using (ShimsContext.Create())
    {
        // Arrange
        // Use shim for CloudConfigurationManager.GetSetting
        Microsoft.WindowsAzure.Fakes.ShimCloudConfigurationManager.GetSettingString = (key) =>
        {
            return "mockedSettingValue";
        };
                
        // Create the fake queue:
        // In the completed application, queue would be a real one:
        bool wasCreateFromConnString = false;
        Order orderSent = null;
        IMicrosoftAzureQueue queue =
                new OrderWebRole.Queue.Fakes.StubIMicrosoftAzureQueue() // Generated by Fakes.
                {
                    // Define each method:
                    // Name is original name + parameter types:
                    InitializeFromConnectionStringStringString = (connectionString, queueName) => {
                        wasCreateFromConnString = true;
                    },
                    SendOrder = (order) => {
                    orderSent = order;
                    }
                };

        // Component under test
        OrderController controller = new OrderController(queue);

        // Act
        Order inputOrder = new Order()
        {
            OrderId = System.Guid.NewGuid(),
            Description = "A mock order"
        };
        ViewResult result = controller.Create(inputOrder) as ViewResult;

        //Assert
        Assert.IsTrue(wasCreateFromConnString);
        Assert.AreEqual("OrderCreated", result.ViewName);
        Assert.IsNotNull(orderSent);
        Assert.AreEqual(inputOrder.OrderId, orderSent.OrderId);
        Assert.AreEqual(inputOrder.Description, orderSent.Description);
    }
}

Web ロールは、注文をキューに追加する処理を行います。ここで、Service Bus キューから注文を取得して処理するワーカー ロールのテスト方法について考えてみましょう。このワーカー ロールの Run メソッドは、注文のキューを定期的にポーリングし、処理します。

private IMicrosoftAzureQueue queue;
public WorkerRole()
{
    queue = new MicrosoftAzureQueue();
}

public WorkerRole(IMicrosoftAzureQueue queue)
{
    this.queue = queue;
}
public override void Run()
{
    try
    {
        string connectionString = CloudConfigurationManager.GetSetting("Microsoft.ServiceBus.ConnectionString");
        string queueName = "ProcessingQueue";
               
        queue.InitializeFromConnectionString(connectionString, queueName);
            
        queue.CreateQueueIfNotExists();
              
        while (true)
        {
            Thread.Sleep(2000);
            //Retrieve order from Service Bus Queue  
            TryProcessOrder(queue);
        }
    }
    catch (Exception ex)
    {
        if (queue != null)
            queue.Close();
        System.Diagnostics.Trace.TraceError(ex.Message);
    }
}

このテストでは、ルーチンがメッセージを正しく取得していることを確認します。ワーカー ロールの Run メソッドの完成した単体テストを次に示します。

[TestMethod]
public void Test_WorkerRole_Run()
{
    // Shims can be used only in a ShimsContext:
    using (ShimsContext.Create())
    {
        Microsoft.WindowsAzure.Fakes.ShimCloudConfigurationManager.GetSettingString = (key) =>
        {
            return "mockedSettingValue";
        };

        // Arrange 
        bool wasEnsureQueueExistsCalled = false;
        int numCallsToEnsureQueueExists = 0;

        // Create the fake queue:
        // In the completed application, queue would be a real one:
        bool wasConnectionClosedCalled = false;
        bool wasCreateFromConnString = false;
        bool wasReceiveCalled = false;
        int numCallsToReceive = 0;

        bool wasCompleteCalled = false;
        int numCallsToComplete = 0;
        IMicrosoftAzureQueue queue =
                new OrderWebRole.Queue.Fakes.StubIMicrosoftAzureQueue() // Generated by Fakes.
                {

                    // Define each method:
                    // Name is original name + parameter types:
                    InitializeFromConnectionStringStringString = (connectionString, queueName) =>
                    {
                        wasCreateFromConnString = true;
                    },
                    CreateQueueIfNotExists = () =>
                    {
                        wasEnsureQueueExistsCalled = true;
                        numCallsToEnsureQueueExists++;
                    },
                    Receive = () =>
                {
                    wasReceiveCalled = true;
                    if (numCallsToReceive >= 3) throw new Exception("Aborting Run");
                    numCallsToReceive++;
                    Order inputOrder = new Order()
                    {
                        OrderId = System.Guid.NewGuid(),
                        Description = "A mock order"
                    };
                    return new BrokeredMessage(inputOrder);
                },
                    Close = () =>
                    {
                        wasConnectionClosedCalled = true;
                    }

                };


        Microsoft.ServiceBus.Messaging.Fakes.ShimBrokeredMessage.AllInstances.Complete = (message) =>
        {
            wasCompleteCalled = true;
            numCallsToComplete++;
        };

        WorkerRole workerRole = new WorkerRole(queue);

        //Act
        workerRole.Run();

        //Assert
        Assert.IsTrue(wasCreateFromConnString);
        Assert.IsTrue(wasConnectionClosedCalled);
        Assert.IsTrue(wasEnsureQueueExistsCalled);
        Assert.IsTrue(wasReceiveCalled);
        Assert.AreEqual(1, numCallsToEnsureQueueExists);
        Assert.IsTrue(numCallsToReceive > 0);
        Assert.IsTrue(wasCompleteCalled);
        Assert.IsTrue(numCallsToComplete > 0);
        Assert.AreEqual(numCallsToReceive, numCallsToComplete);

    }
}

生成された Fake 型の AllInstances プロパティの委任を設定する必要があります。委任を使用して、実際の Fake 型を持つインスタンスを作成すると、委任を定義したメソッドを迂回します。

この例では、元のインスタンスの Run メソッドを使用しますが、CreateQueue および TryProcessOrder インスタンス メソッドの迂回を用意します。このコードでは、Run メソッドが保持する永久ループを事前に定義した時点で終了するように例外をスローします。

ヘルパー型を挿入するのではなく、単に MessageSender/MessageReceiver と Service Bus SDK の関連クラスを使用すればよいのでは、という疑問を持たれるかもしれません。実際の Service Bus を呼び出させないようにコードを完全に分離するには、2 つの選択肢があります。

  • Microsoft.ServiceBus 名前空間の抽象クラスから継承された Fakes を作成します。

  • Fakes を使用すると、すべてについてモックの種類を作成できます。

いずれのアプローチでも問題は複雑です。両方のアプローチを使用すると、最終的に TokenProviderQueueClient のようなクラスにリフレクションされます。リフレクションによって次の問題が発生します。

  • すべての必要なオーバーライドを公開する、抽象型から派生した型を作成する必要があります。

  • 実際のバージョンでは、このようなクラスが依存している内部型を公開する必要があります。

  • 内部型の場合、実際の Service Bus の依存関係を適切な方法で実行するように、コンストラクターまたはファクトリ メソッドを作り直す必要があります。

独自のヘルパー型を挿入することをお勧めします。実際の Service Bus のコードのモック作成、迂回、および分離に必要な処理は以上です。

単体テストの検証内容を分析するために、コード カバレッジ データを検証します。MS Test を使用して両方の単体テストを実行すると、合格したことがわかります。また、[テスト エクスプローラー] ダイアログから、関連する実行内容も確認できます。

図 2

図 2

テスト エクスプローラーからテスト用にコード カバレッジを実行できます。テストを右クリックし、[選択されたテストのコード カバレッジの分析] を選択します。結果は、[コード カバレッジの結果] ウィンドウに表示されます。コード カバレッジのデータ コレクションをアクティブ化するには、[ソリューション項目] で Local.testsettings ファイルを構成します。このファイルを開くと、[テストの設定] エディターが開始されます。

Local.testsettings ファイルが含まれないソリューションがある場合は、次の手順でソリューションに追加します。

  1. [新しい項目の追加] をクリックします。

  2. [テスト] を選択し、[テストの設定] をクリックします。

    図 3
    図 3

  3. [データと診断] タブをクリックし、[コード カバレッジ] 行の右にあるチェック ボックスをオンにします。

  4. 次に、[構成] 図 A をクリックします。

  5. [コード カバレッジの詳細] ウィンドウで、テストするすべてのアセンブリを選択し、[OK] をクリックします。

    図 4
    図 4

  6. [テストの設定] エディターを閉じるには、[適用して閉じる] をクリックします。

  7. テストを再実行し、[コード カバレッジ] をクリックします。コード カバレッジの結果は、図 5 のようになります。

    図 5
    図 5

  8. [コード カバレッジの色分けを表示] アイコンをクリックし、グリッドのメソッドまで移動します。

  9. メソッドをダブルクリックします。ソース コードには、テストされた領域を示す色で表示されます。緑色は、コードがテストされたことを示します。灰色は、コードが部分的にテストされたか、テストされていないことを示します。

    図 6
    図 6

単体テストを手動で作成にはコストがかかりますが、Code Digger を使用して単体テストを適切にアップグレードすることもできます。Code Digger は、考慮していなかったようなパラメーター値も試行します。Code Digger のインストール後に、コード エディターでメソッドを右クリックし、[入力/出力テーブルの生成] を選択して、メソッドを調べることができます。

図 7

図 7

少し待つと Code Digger の処理が完了し、結果が一定になります。たとえば、図 8 は、ワーカー ロールの TryProcessOrder メソッドで Code Digger を実行した結果を示しています。Code Digger は、結果が例外になる 1 つのテストを作成できる点に注目してください。また、Code Digger は、その例外 (キュー パラメーターの null 値) を生成するために作成した入力内容も表示されます。こちらの方がより重要な情報です。

図 8

図 8

表示:
© 2016 Microsoft