2012年1月24日火曜日

Workflow Foundation におけるユニットテスト

CodePlex にて Workflow Foundation 用のユニットテストライブラリ「Microsoft.Activities.UnitTesting」が公開されています。Workflow Foundation ではアクティビティの実行等、通常のロジック実行とは異なる部分もあり、そのまま NUnit 等のユニットテストを行うのは不便な状況でした。ですがユニットテスト用のこのライブラリを利用する事で、今までと同様にユニットテストを行う事ができます。

ライブラリ自身は CodePlex からダウンロードし適当なフォルダに展開する事もできますが、最近の流行りである NuGet 上でも提供されています。Visual Studio (または Visual Web Developer)から直接インストールさせることも可能です。なお、拡張機能マネージャコンソールから、インストールを行う場合には以下のコマンドを入力します。

   1: PM> install-package Microsoft.Activities.UnitTesting

NuGet からインストールを行った場合には、.NET 4 用と .NET 401(Platform Update 1 対応)用が双方インストールされます。

ライブラリ自身は Visual Studio 標準のテスト機能、または NUnit からも利用できます。もちろん Express 環境のみで開発されている場合は NUnit の利用に限定されます。ライブラリにより色々と機能が提供されているのですが、まずは最低限必要となるアクティビティのテストとワークフローのテストに絞った形で利用してみます。

アクティビティ用プロジェクトとテスト用プロジェクトを作成します。このあたりは Workflow Foundation に限った話ではなく、今までのユニットテストと全く同一の流れです。プロジェクトを作成しましたら、テスト用プロジェクトがアクティビティ用プロジェクトを参照するように設定し、また追加で Workflow Foundation 用アセンブリや、ユニットテスト用ライブラリを参照設定します。最低限必要な参照設定は次のようになります。

Reference

赤枠で囲んだ部分が「Microsoft.Activities.UnitTesting」で提供されるアセンブリと NUnit で提供されるアセンブリです。この状態からテストケースをコーディングしていきます。

まずはアクティビティのユニットテストです。今回作成してみようとしているのは、AVal、BVal 二つのプロパティ(InArgument)で値を受取り、演算を行った結果を Results プロパティ(OutArgument)で返却する、というものです。

   1: Imports NUnit.Framework
   2: Imports Microsoft.Activities.UnitTesting
   3: Imports WFApplicationSample
   4:  
   5: <TestFixture()>
   6: Public Class UnitTestWFSample
   7:  
   8:     <TestCase(1, 3, 3)>
   9:     <TestCase(2, 5, 10)>
  10:     <TestCase(3, Nothing, 0)>
  11:     <TestCase(Nothing, Nothing, 0)>
  12:     Public Sub TestActivity(ByVal a As Integer, ByVal b As Integer, ByVal res As Integer)
  13:  
  14:         Dim wfInvoker As WorkflowInvokerTest = Nothing
  15:  
  16:         'テスト用アクティビティの作成
  17:         Dim testActivity As New SampleActivity With {.AVal = a, .BVal = b}
  18:         wfInvoker = New WorkflowInvokerTest(testActivity)
  19:         wfInvoker.TestActivity()
  20:  
  21:         wfInvoker.AssertOutArgument.AreEqual("Results", res)
  22:  
  23:     End Sub
  24:  
  25: End Class

まだアクティビティを実装していませんので、ビルドは通らない状態です。そのためまずはアクティビティの枠だけは用意してしまいます。なお、上記テストロジックは最終形ですので、TestCase 属性を使用して複数パターンのテストを行うように記述しています。

ここで利用している 「Microsoft.Activities.UnitTesting」ライブラリは WorkflowInvokerTest クラスです。これは WorkflowInvoker のユニットテスト用クラスで、AssertOutArgument プロパティなどユニットテストに必要なプロパティやメソッドが拡張されています。何かしらのユニットテストを行った事があるのであれば、プロパティやメソッドの名称を見ると大体掴めると思います。

   1: Imports System.Activities
   2:  
   3: Public Class SampleActivity
   4:     Inherits CodeActivity
   5:  
   6:     Public Property AVal As InArgument(Of Integer)
   7:     Public Property BVal As InArgument(Of Integer)
   8:  
   9:     Public Property Results As OutArgument(Of Integer)
  10:  
  11:     Protected Overrides Sub Execute(context As System.Activities.CodeActivityContext)
  12:  
  13:     End Sub
  14:  
  15: End Class

プロパティだけ実装された状態ですが、この状態であればビルドが通ります。ビルド後 NUnit にてテストを行うと当然失敗します。この状態をスタートとして、アクティビティの実装~テストを行っていく事になります。今回はサンプルですので、次のようにコーディングしました。

   1: Imports System.Activities
   2:  
   3: Public Class SampleActivity
   4:     Inherits CodeActivity
   5:  
   6:     Public Property AVal As InArgument(Of Integer)
   7:     Public Property BVal As InArgument(Of Integer)
   8:  
   9:     Public Property Results As OutArgument(Of Integer)
  10:  
  11:     Protected Overrides Sub Execute(context As System.Activities.CodeActivityContext)
  12:  
  13:         Dim a = context.GetValue(AVal)
  14:         Dim b = context.GetValue(BVal)
  15:  
  16:         Dim res = a * b
  17:  
  18:         context.SetValue(Results, res)
  19:  
  20:     End Sub
  21:  
  22: End Class

渡されたプロパティ二つを乗算して返却するというものです。このようにコーディングした後、テストを行うと全てのケースで正常終了します。

ActivitiyTest

これに近い形でワークフローのテストを行います。アクティビティとワークフローのテストで異なるのは、引数の存在です。今回は次のようなワークフローを作成してテストを行ってみます。

WFUnitTest

Assign アクティビティにて引数二つを掛け合わせ、結果を引数として返却するものです。これをテストするロジックは次のようになります。

   1: <TestCase(1, 3, 3)>
   2: <TestCase(5, 9, 45)>
   3: Public Sub TestWorkflow(ByVal a As Integer, ByVal b As Integer, ByVal res As Integer)
   4:  
   5:     'テスト用引数の作成
   6:     Dim inArgs As New Dictionary(Of String, Object)
   7:     inArgs.Add("AArg", a)
   8:     inArgs.Add("BArg", b)
   9:  
  10:     'テスト用ワークフローの作成
  11:     Dim xamlInject As New XamlInjector("D:\WFTest.xaml")
  12:     Dim wfInvoker As New WorkflowInvokerTest(xamlInject.GetActivity)
  13:  
  14:     Dim resArgs = wfInvoker.TestActivity(inArgs)
  15:     For Each child In resArgs.ToList
  16:         Console.WriteLine(child.Key + " : " + child.Value.ToString)
  17:     Next
  18:     Assert.AreEqual(resArgs("Results"), res)
  19:  
  20: End Sub

引数自体の考え方は WorkflowInvoker を利用したワークフローの実行時と何ら変わりはありません。アクティビティの時と異なっているのは、「ワークフローの読み込み」「結果の受け取り」の部分です。

ワークフローの読み込みは XamlInjector クラスを利用して読み込みます。今回このクラスは読み込みにしか利用していませんが、読み込んだワークフローの xaml に対して置換を行うなど、他にも機能が用意されています。読み込んだワークフローは、XamlInjector.GetActivity メソッドにて Activity として取得できますので、この値を WorkflowInvokerTest クラスに引き渡します。

そして実行するのですが、出力される引数(方向が Out)の物がメソッド呼び出しの結果として受け取れますので、これに対して結果のチェックを行う形になります。

UnitTestResults

テストを実行するとこのように動作します。

以上のように Workflow Foundation の開発時におけるユニットテストを行う場合、「Microsoft.Activities.UnitTesting」ライブラリを利用する事でその他クラスのテストとほぼ同じように進める事ができます。属性を変化させれば Visual Studio の単体テスト用になりますので、NUnit 含めユニットテストを行う場合は是非利用してみてください。

0 件のコメント:

コメントを投稿