• Save
CLRH_120414_WFTDD
Upcoming SlideShare
Loading in...5
×
 

Like this? Share it with your network

Share

CLRH_120414_WFTDD

on

  • 718 views

2012/04/14 CLR/H #69 にて利用したスライド

2012/04/14 CLR/H #69 にて利用したスライド

Statistics

Views

Total Views
718
Views on SlideShare
713
Embed Views
5

Actions

Likes
0
Downloads
0
Comments
0

2 Embeds 5

https://twimg0-a.akamaihd.net 4
https://twitter.com 1

Accessibility

Categories

Upload Details

Uploaded via as Microsoft PowerPoint

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

CLRH_120414_WFTDD Presentation Transcript

  • 1. Workflow Foundation 4into Test Driven Develop Ahf(小尾 智之)
  • 2. 自己紹介VB と SQL Server がメインの何でも屋インディー団体を含むプロレス全般と クマ(クマー含む)やもふもふしたもの好き 黙って宇宙戦艦ヤマト2199 は見ておけWorkflow Foundation ソムリエが目標@IT エンジニアライフで色々書いてます
  • 3. おさらいWorkflow Foundation とは
  • 4. おさらい:WF の特徴・概念アクティビティと呼ばれる要素の集合振舞い・手順をモデルとして保存・実行 XML (WF3.x は xoml、WF4 は xaml)で 保存される長期間における実行をサポートしている 永続化、ブックマーク、アンロード 標準で SQL Server を容易に利用できるデザイナ上でモデル作成する方法とロジッ ク上で全てを行う方法があるGUI / CUI どちらも対応可能
  • 5. おさらい:開発環境についてExpress ではそのままだと無理  デザインコンポーネント拡張が必要な為  エディタを自前で用意すれば可能WF 3.5  Visual Studio 2005 + ExtensionS for Windows WF  Visual Studio 2008 Standard 以降WF 4  Visual Studio 2010 Professional 以降
  • 6. おさらい:WF3.5 と 43.5 と 4 はクラス構成も 変更され全くの別物  基底クラスから全て異なる3.5 で実装されたアク ティビティを4 環境で 利用する特殊措置を提供  Interop アクティビティ  CodePrex で移行キットを 提供…  基本は作り直し  WF 3.x を利用してないのなら 気にしないのが幸せです
  • 7. おさらい:WF4 での主な改善点WF 3.x よりもパフォーマンス向上 全面的に向上しすぎ 今まではなんだったんだ 参考:MSDN:Windows Workflow Foudation 4 のパフォーマンスhttp://msdn.microsoft.com/ja-jp/library/gg281645.aspx
  • 8. WF でユニットテスト
  • 9. WF の単体テスト Visual Studio で利用する単体テスト プロジェクトでは WF の単体テストを 対象にしていない テストプロジェクトから参照設定を行うと怒られる Visual Studio 11 においても同様 クラスライブラリ プロジェクトを 利用する方法で回避
  • 10. WF 固有の問題点アクティビティを WF ランタイム機実行する方法が複 能 数 としての永続化入力待機等に用い ロジックがあると る は限らないブックマーク機能
  • 11. アクティビティの実行 WFアクティビティの実行方法は3通り  最もシンプルな実行方法。WorkflowInvoker  ブックマークが利用できない  ワークフロー上のイベントWorkflowApplication トラップなどが行える  WorkflowApplication + WebWorkflowServiceHost サービスとして公開 実際には IIS でのホスティングも関係
  • 12. WF による永続化標準機能として SQL Server による 永続化をサポートカスタマイズによる SQL Server 以外の DB やテキストファイル等へ永続化も可能永続化機能はワークフローの内容と 直接的な関係が薄い待ち状態の際に永続化する制御は ワークフロー個別に記載するのではなく WF ランタイム側にて制御
  • 13. ブックマーク反応が戻されるまでワークフローの実行を 一時停止し待機状態にするための仕組み 状態遷移型ワークフロー等でよく利用バックグランドでワークフローを 起動させておき、フォアグランド側の アプリから通信を行わせる形にて利用する
  • 14. ロジックのないアクティビティワークフロー = アクティビティ であるので既存アクティビティを ワークフロー上に設置しただけのものも アクティビティとなるコードが存在しないアクティビティを 作ることができるVisual Studio 等でビルドすれば 通常のクラスとして利用できる
  • 15. WF で TDDNUnit や MSTest、xUnit など多くの テストフレームワークがあるので利用アクティビティやワークフローの 実行ロジックが毎回必要で結構な量に  WF ランタイムを通した挙動を テストする必要がある  ライブラリ化で対応は可能サービスのテストも必要  セルフホストのコードを用意するか  IIS や IIS Express を利用するか
  • 16. Microsoft.Activities.UnitTestingCodePlex 上で公開されている WF 用 ユニットテストライブラリ作者は Microsoft の Ron Jacobs 氏(http://blogs.msdn.com/b/rjacobs/) AppFabric 開発チームのシニア プログラムマネージャらしい利用する事で簡易に WF における ユニットテストが行えるようになる
  • 17. TDDの流れ 仕様の作成 テストの作成 テスト コーディング リファクタリング繰り返し行う
  • 18. TDDの流れアクティビティの開 仕様の作成 テストの作成発 テスト コーディング リファクタリング ワークフローの開発 ワークフローの開発 アクティビティ = ワークフロー
  • 19. 余談:VWD2010 の設定 Visual Web Developer 2010 で Nunit を 利用する場合は少し設定を行っておくと楽に 1. [ツール]-[外部ツール]にて NUnit を登録 2. [ツール]-[カスタマイズ]にてプロジェクトの 右クリックメニューに NUnit を登録 直接ソリューションエクスプローラ上から プロジェクト指定した形で NUnit が起動できる
  • 20. TDD による WF 開発
  • 21. テストシナリオ渡された文字列の文字数をカウント するアクティビティの作成ワークフローに変数または引数を2つInParam1:入力文字列用の引数OutParam1:文字数のカウント結果
  • 22. プロジェクトの作成 アクティビティライブラリと して新規プロジェクト作成VWD の場合はクラスライブラリにて作成
  • 23. テスト用プロジェクトの追加 作成単体テストプロジェクトでは クラスライブラリとして作成すWF の単体テストを行えない る 参照の追加アクティビティライブラリへの MSTest または NUnit 等への参 参照を追加 照を追加 MSTest を利用する場合は Microsoft.VisualStudio.QualityTools.UnitTestFramework への参照を追加する(参照設定ダイアログで検索すると表示される) Nunit を利用する場合は Nunit.framework への参照を追加する
  • 24. NuGet でパッケージの追加 Microsoft.Activities.UnitTesting を NuGet にて追加するPM>Install-Package Microsoft.Activities.UnitTesting Professional 以上の VS か VWD の場合は NuGet にて それ以外の Express な場合は CodePlex よりダウンロードして参照を追加
  • 25. テストの作成Imports Nunit.FrameworkPublic Class UnitTest1 <Test()> Public Sub TestMethod1() End SubEnd Class NUnitImports Microsoft.VisualStudio.TestTools.UnitTesting<TestClass()>Public Class UnitTest1 <TestMethod()> Public Sub TestMethod1() End SubEnd Class MSTest
  • 26. テストの実行ビルド後に一度テストを実行問題なく正常終了する事を確認
  • 27. テストの作成まずは要件を満たすテストを作成するその際ビルドを通すために、アクティビティの定義は同時に作成する Imports System.Activities Public Class ActivityT Inherits CodeActivity Public Property InParam1 As InArgument(Of String) Public Property OutParam1 As OutArgument(Of Integer)Integer) Protected Overrides Sub Execute(context As CodeActivityContext) End Sub End Class
  • 28. ヘルパークラス テスト用ヘルパークラスでは NUnit 等と同様のメソッドや プロパティが定義されている AssertOutArgument プロパティなど WF 特有の プロパティが追加された形 何らかのテストフレームワークを 利用したことがあれば特に悩む ことはないと思われる
  • 29. テスト用ロジックの記載<Test()>Public Sub TestMethod1() Const ORIGINAL_WORD = "Sample Word" 想定される結果 Const RESULTVALUE = 11 テストするアクティビティの生成 Dim testActivity As New TDDSampleLib.ActivityT 実行用 WorkflowInvoker の生成 Dim testInvoker As WorkflowInvokerTest = Nothing Try アクティビティへパラメータを設定 Dim inArgs As New Dictionary(Of String, String) inArgs.Add("InParam1", ORIGINAL_WORD) 実行用 WorkflowInvoker の生成 testInvoker = New WorkflowInvokerTest(testActivity) 実行 testInvoker.TestActivity(inArgs) ワークフローの実行 結果の確認 testInvoker.AssertOutArgument.AreEqual("OutParam1", RESULTVALUE) Finally testInvoker.Tracking.Trace() End TryEnd Sub
  • 30. テスト失敗を確認ビルド後テストを実行し、テストが予定通り「失敗」する事を確認する ここからがスタート
  • 31. コーディングテストを通過できるようアクティビティのコーディングを行うImports System.ActivitiesPublic Class ActivityT Inherits CodeActivity Public Property InParam1 As InArgument(Of String) Public Property OutParam1 As OutArgument(Of Integer) Protected Overrides Sub Execute(context As CodeActivityContext) Dim inString = context.GetValue(Me.InParam1) context.SetValue(Me.OutParam1, inString.Length) End SubEnd Class規則上は「テストを通過するように単純に」処理を書くよう言われるがあまり正直にやりすぎるのも微妙なところ・・・
  • 32. テストとトラッキングコーディング後にテストを実行し、テストに通過する事を確認Finally Tracking.Trace を行う事で testInvoker.Tracking.Trace() アクティビティのトラッキング情報がEnd Try 出力される
  • 33. リファクタリング サロゲートペア文字での<TestCase("Sample Word", 11)> テストを追加<TestCase("𠮟 る", 2)>Public Sub TestMethod2(ByVal orgWord As String, ByVal result As Integer) テストケースの追加 テストするアクティビティの生成 Dim testActivity As New TDDSampleLib.ActivityT 実行用 WorkflowInvoker の生成 Dim testInvoker As WorkflowInvokerTest = Nothing Try アクティビティへプロパティを設定 testActivity.InParam1 = orgWord 実行用 WorkflowInvoker の生成 testInvoker = New WorkflowInvokerTest(testActivity) 実行 testInvoker.TestActivity() 結果の確認 testInvoker.AssertOutArgument.AreEqual("OutParam1", result) Finally testInvoker.Tracking.Trace() End Try テストは失敗するのでEnd Sub コードを修正する
  • 34. <TestMethod()> MSTestの場合はTestCase属性 Public Sub TestMethod1() TestInPrivate("Sample String", 13) (ROWテスト)が利用できない TestInPrivate("𠮟 る", 2) End Sub Private Sub TestInPrivate(ByVal orgWord As String, ByVal result As Integer) テストするアクティビティの生成 Dim testActivity As New TDDSampleLib.ActivityT 実行用 WorkflowInvoker の生成 Dim testInvoker As WorkflowInvokerTest = Nothing Try アクティビティへプロパティを設定 testActivity.InParam1 = orgWord 実行用 WorkflowInvoker の生成 testInvoker = New WorkflowInvokerTest(testActivity) 実行 testInvoker.TestActivity() 結果の確認 testInvoker.AssertOutArgument.AreEqual("OutParam1", result) Finally testInvoker.Tracking.Trace() End Try End Sub 独自に同様の属性を作成するか、親子関係の テストメソッドとして作成するかで対応MSDN Blog:Visual Studio Team Test(http://blogs.msdn.com/b/vstsqualitytools/)にて独自に属性を拡張する対応案について記載あり(http://blogs.msdn.com/b/vstsqualitytools/archive/2009/09/04/extending-the-visual-studio-unit-test-type-part-2.aspx)
  • 35. Imports System.ActivitiesPublic Class ActivityT Inherits System.Activities.CodeActivity Public Property InParam1 As InArgument(Of String) Public Property OutParam1 As OutArgument(Of Integer) Protected Overrides Sub Execute(context As CodeActivityContext) Dim inString = context.GetValue(Me.InParam1) Dim inStrInfo = New Globalization.StringInfo(inString) context.SetValue(Me.OutParam1, inStrInfo.LengthInTextElements) End SubEnd Classリファクタリング後再度テストを行いテストに通過するのを確認
  • 36. ブックマークのテスト<Test()> WorkflowApplicationTest をPublic Sub TestBookmarkWorkflow() 利用したテストのサンプル Const BOOKMARKNAME = "TestBookmark1" Const SENDVALUE = "1" テストするアクティビティの生成 Dim testActivity As New ActivitySample 実行用 WorkflowInvoker の生成 Dim testAppliaction As WorkflowApplicationTest(Of ActivitySample) = Nothing Try 実行用 WorkflowApplication の生成 testAppliaction = WorkflowApplicationTest.Create(testActivity) 実行 testAppliaction.TestActivity() ブックマークの確認 Assert.IsTrue(testAppliaction.Bookmarks.Contains(BOOKMARKNAME)) Assert.AreEqual(BookmarkResumptionResult.Success, testAppliaction.TestWorkflowApplication.ResumeBookmark(BOOKMARKNAME, SENDVALUE)) 終了まで待機 Assert.IsTrue(testAppliaction.WaitForCompletedEvent) Finally testAppliaction.Tracking.Trace() ブックマークの状態確認と End Try ブックマークの呼出確認がEnd Sub 主なテスト内容となる
  • 37. 永続化のテスト 永続化テスト用ヘルパークラス MemoryStore が用意されている  Microsoft.Activities.UnitTesting.Persistance.MemoryStore 現時点では WorkflowServiceTestHost で 自動利用されるだけで他からは利用できない  WorkflowApplicationTest クラスに永続化を 利用する仕組みが用意されていない SQL Server に永続化用環境を構築して テストするのがベター永続化環境構築用スクリプトはデフォルトで用意されている
  • 38. この作業の繰り返しにてアクティビティの実装を行う WFにおける もう1つの 実装パターン Xamlのみで作成する 純粋なアクティビティ
  • 39. Xaml のみで作成するアクティビティのサンプル
  • 40. <Activity mc:Ignorable="sap" x:Class="Sequence1" xmlns="http://schemas.microsoft.com/netfx/2009/xaml/activities" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mva="clr-namespace:Microsoft.VisualBasic.Activities;assembly=System.Activities" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:s1="clr-namespace:System;assembly=System" xmlns:s2="clr-namespace:System;assembly=System.Core" xmlns:s3="clr-namespace:System;assembly=System.ServiceModel" xmlns:sa="clr-namespace:System.Activities;assembly=System.Activities" xmlns:sap="http://schemas.microsoft.com/netfx/2009/xaml/activities/presentation" xmlns:scg="clr-namespace:System.Collections.Generic;assembly=mscorlib" xmlns:sg="clr-namespace:System.Globalization;assembly=mscorlib"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> <x:Members> <x:Property Name="InParam1" Type="InArgument(x:String)" /> <x:Property Name="OutParam1" Type="OutArgument(x:Int32)" /> </x:Members> <this:Sequence1.InParam1> このような形で xml 形式 <InArgument x:TypeArguments="x:String"> <Literal x:TypeArguments="x:String" Value="" /> として記述できる </InArgument> </this:Sequence1.InParam1><sap:VirtualizedContainerService.HintSize>306,324</sap:VirtualizedContainerService.HintSize> <mva:VisualBasic.Settings>Assembly references and imported namespaces serialized as XMLnamespaces</mva:VisualBasic.Settings> <Sequence DisplayName="シーケンス" sap:VirtualizedContainerService.HintSize="266,284"> <Sequence.Variables> <Variable x:TypeArguments="sg:StringInfo" Name="InStrInfo" /> </Sequence.Variables>
  • 41. Visual Studio 上では xaml ファイルでのワー クフローもクラスとして認識するExpress ではクラスとして認識しない
  • 42. XamlInjector での呼出<TestMethod()> Public Sub TestMethodFromXaml() Const ORIGINAL_WORD = "Sample Word" Const RESULTVALUE = 11 XamlInjector にて動的に ワークフローを呼び出し Dim xl As New XamlInjector("Activity1.xaml") Dim testActivity = xl.GetActivity アクティビティを取得 Dim testInvoker As WorkflowInvokerTest = Nothing Try Activity クラスとして ワークフローへプロパティを設定 Dim inArgs As New Dictionary(Of String, Object) 取得できるので中身を inArgs.Add("InParam1", ORIGINAL_WORD) 気にしなくてよい 実行用 WorkflowInvoker の生成 testInvoker = New WorkflowInvokerTest(testActivity) 実行 Dim result = testInvoker.TestActivity(inArgs) 結果の確認 Assert.IsTrue(result.ContainsKey("OutParam1")) Assert.AreEqual(RESULTVALUE.ToString, result("OutParam1").ToString) Finally testInvoker.Tracking.Trace() End Try End Sub
  • 43. XamlInjector での呼出<TestMethod()> Public Sub TestMethodFromXaml() Const ORIGINAL_WORD = "Sample Word" アクティビティのプロパティ Const RESULTVALUE = 11 設定と異なり引数での受渡しは Dim xl As New XamlInjector("Activity1.xaml") Dim testActivity = xl.GetActivity Dictionary を利用する Dim testInvoker As WorkflowInvokerTest = Nothing Try 引数は大文字小文字を ワークフローへプロパティを設定 Dim inArgs As New Dictionary(Of String, Object) 判別するので注意 inArgs.Add("InParam1", ORIGINAL_WORD) 実行用 WorkflowInvoker の生成 testInvoker = New WorkflowInvokerTest(testActivity) 実行 Dim result = testInvoker.TestActivity(inArgs) 結果の確認 Assert.IsTrue(result.ContainsKey("OutParam1")) Assert.AreEqual(RESULTVALUE.ToString, result("OutParam1").ToString) Finally testInvoker.Tracking.Trace() End Try End Sub
  • 44. ワークフローサービスのテストテスト用サービスホスト• テスト方法はWCFクライアントでのアクセスと同様の 手順• 実際にサービスとして起動できる必要がある• Express の場合は Web 参照にてワークフローサービス の 情報を取得する必要があるモック• 送受信アクティビティを置換• サービスとして起動しなくてよい• 置換するアクティビティの数だけテストロジック量が増 える
  • 45. テスト対象の ワークフローサービス渡された文字列の文字数をカウントし返却するシンプルなサービス
  • 46. <"Sample Word", 11)> モックを利用したテストのサンプル<TestCase("𠮟 る", 2)>Public Sub TestServiceUseMock(ByVal orgWord As String, ByVal result As Integer) Dim xl As New XamlInjector("TestService.xamlx") 送受信アクティビティの置換 xl.ReplaceAll(GetType(Receive), GetType(ReceiveStub)) xl.ReplaceAll(GetType(Send), GetType(SendStub)) ダミーのメッセージを設定 Dim stubExtension As New MessagingStubExtension OperationName と stubExtension.EnqueueReceive( ServiceContracetName XName.Get("{http://tempuri.org/}IService"), "LengthCount", orgWord) の設定から記載 Dim svHost = WorkflowInvokerTest.Create(xl.GetWorkflowService.Body) svHost.Extensions.Add(stubExtension) Try svHost.TestActivity() Assert.AreEqual(2, stubExtension.Messages.Count) Assert.AreEqual(result, stubExtension.Messages(1).Content) Finally svHost.Tracking.Trace() End TryEnd Sub
  • 47. <"Sample Word", 11)> モックを利用したテストのサンプル<TestCase("𠮟 る", 2)>Public Sub TestServiceUseMock(ByVal orgWord As String, ByVal result As Integer) Dim xl As New XamlInjector("TestService.xamlx") 送受信アクティビティの置換 xl.ReplaceAll(GetType(Receive), GetType(ReceiveStub)) xl.ReplaceAll(GetType(Send), GetType(SendStub)) ダミーのメッセージを設定 WorkflowInvokerTest で Dim stubExtension As New MessagingStubExtension stubExtension.EnqueueReceive( 通常通りにテストを行う XName.Get("{http://tempuri.org/}IService"), "LengthCount", orgWord) Dim svHost = WorkflowInvokerTest.Create(xl.GetWorkflowService.Body) svHost.Extensions.Add(stubExtension) Try svHost.TestActivity() Assert.AreEqual(2, stubExtension.Messages.Count) Assert.AreEqual(result, stubExtension.Messages(1).Content) Finally svHost.Tracking.Trace() End TryEnd Sub
  • 48. まとめユニットテスト用ライブラリを利用する事で、 通常とほぼ同様に WF も TDD を行えますTDD で開発すると安心に繋がります特殊な要素が色々ありますが WF を 使ってみてください……
  • 49. ご清聴ありがとうございました