WF4.0進行單元測試

作者: 麒麟  來源: 博客園  發布時間: 2010-10-14 07:13  閱讀: 1238 次  推薦: 0   原文鏈接   [收藏]  
摘要:本文通過舉例說明在WF4.0中如何進行單元測試。

  1、簡單的WF4.0活動測試

  如果是一個簡單的WF4.0活動,是那種沒有帶BookMark,也不是messaging活動,也不是長時間運行的活動。使用WorkflowInvoker進行單元測試將非常的方便。

  下面,我們以一種情況為例子:流程中只包含了兩個加數相加的操作,然后就將結果返回。流程如下圖所示:

  最簡單的方法是通過Workflow Invoker進行測試。

 
[TestMethod]
public void ShouldAddGoodSum()
{
var output
= WorkflowInvoker.Invoke(new GoodSum() {x=1, y=2});

Assert.AreEqual(
3, output["sum"]);
}

  如果這個測試通過,就是說這個流程的輸出參數集合中包含一個 Int32的sum參數,值等于3。有下面幾種情況可能需要我們進行測試:

  1、沒有名為sum這個輸出參數。

  2、有一個sum輸出參數,但是類型不是Int32。

  3、有一個sum輸出參數,類型也是Int32,但是值不是我們期待的(3)。

  這樣我們能清楚流程哪出了問題。我們修改一下流程,將輸出的參數sum改成Sum。在VB中,沒有什么問題,但是在C#中sum!=Sum。通過上面測試代碼你會得到下面的錯誤提示。

  但是異常信息提示的太簡單,我們不知道到底是那個參數出現了問題。有更好的辦法嗎?有,使用WorklowTestHelper。

  使用WorklowTestHelper進行單元測試:

  1、下載WorkflowTestHelper

  2、添加WorkflowTestHelper引用

  3、使用WorkflowTestHelper表達式

  我們將測試代碼改成:

代碼
 
[TestMethod]
public void ShouldAddGoodSumAssertOutArgument()
{
var output
= WorkflowInvoker.Invoke(new GoodSum() { x = 1, y = 2 });

AssertOutArgument.AreEqual(output,
"sum", 3);
}

  再看下出現的異常信息。信息更加明確了。

  AssertOutArgument.AreEqual failed. Output does not contain an argument named <sum>.

  如果類型錯誤,例如我們將流程中的sum修改成string。

  對于Assert.AreEqual我們得到下面錯誤提示:

  Assert.AreEqual failed. Expected:<3 (System.Int32)>. Actual:<3 (System.String)>.

  使用AssertOutArgument,將會得到更確切的錯誤信息:

  AssertOutArgument.AreEqual failed. Wrong type for OutArgument <sum>. Expected Type: <System.Int32>. Actual Type: <System.String>.

  2、對帶有BookMark的流程進行測試

  什么是BookMark?如果你有使用WF4.0開發過,就應該知道BookMark在WF中的地位。簡單的說:如果你的流程需要數據,這些參數可能來自用戶輸入,可能來自宿主環境,BookMark是用來暫停流程,等待響應的。下面是一個書簽活動的代碼:

代碼
 
public sealed class ReadLine : NativeActivity<string>
{
private BookmarkCallback _readCompleteCallback;

[RequiredArgument]

public InArgument<string> BookmarkName { get; set; }

protected override bool CanInduceIdle
{

get { return true; }
}


public BookmarkCallback ReadCompleteCallback
{

get { return _readCompleteCallback ?? (_readCompleteCallback = new BookmarkCallback(OnReadComplete)); }
}


protected override void Execute(NativeActivityContext context)
{

// Inform the host that this activity needs data and wait for the callback
context.CreateBookmark(BookmarkName.Get(context), ReadCompleteCallback);
}


private void OnReadComplete(NativeActivityContext context, Bookmark bookmark, object state)
{

// Store the value returned by the host
context.SetValue(Result, state as string);
}
}

   下面我在流程中使用這個活動,用來等待用戶的輸入。

  對于這個流程,如果你使用WorkflowInvoker,你的應用程序將會一直被掛起。所以你不得不使用WorkflowApplication,以及在你應用程序中處理這些BookMark。

  如何測試這個流程。

  測試這個流程將非常棘手。使用WorkflowTestHelper類庫中的類將會方便很多。這個類就是WorkflowApplicationTest<T>.。這個類可以方便的創建和管理WorkflowApplication。使用非常簡單的代碼就可以處理好流程的所有的事件和結果。測試代碼如下:

代碼
 
[TestMethod]
public void ShouldOutputGreeting()
{

// Arrange
const string expectedFirstName = "Test";
const string expectedLastName = "User";
var expectedGreeting
= string.Format("Hello {0} {1}", expectedFirstName, expectedLastName);
var sut
= WorkflowApplicationTest.Create(new TestReadLine());

// Act

// Run the workflow
sut.TestActivity();

// Wait for the first idle event - prompt for First Name
// will return false if the activity does not go idle within the
// timeout (default 1 sec)
Assert.IsTrue(sut.WaitForIdleEvent());

// Should have a bookmark named "FirstName"
Assert.IsTrue(sut.Bookmarks.Contains("FirstName"));

Assert.AreEqual(BookmarkResumptionResult.Success,
sut.TestWorkflowApplication.ResumeBookmark(
"FirstName", expectedFirstName));

// Wait for the second idle event - prompt for Last Name
Assert.IsTrue(sut.WaitForIdleEvent());

// Should have a bookmark named "LastName"
Assert.IsTrue(sut.Bookmarks.Contains("LastName"));

Assert.AreEqual(BookmarkResumptionResult.Success,
sut.TestWorkflowApplication.ResumeBookmark(
"LastName", expectedLastName));

// Wait for the workflow to complete
Assert.IsTrue(sut.WaitForCompletedEvent());

// Assert
// WorkflowApplicationTest.TextLines returns an array of strings
// that contains strings written by the WriteLine activity
Assert.AreEqual(4, sut.TextLines.Length);
Assert.AreEqual(expectedGreeting, sut.TextLines[
2]);
}

  3、測試WorkflowService

  測試例子:

  定義一個擴展類,用來存儲數據和計算平均數:

 
1 public class AverageExtension
2 {
3 private static readonly List<int> Numbers = new List<int>();
4
5 public static void Reset()
6 {
7 Numbers.Clear();
8 }
9
10 public void StoreNumber(int num)
11 {
12 Numbers.Add(num);
13 }
14
15 public double GetAverage()
16 {
17 return Numbers.Average();
18 }
19 }

  定義一個活動使用此擴展類,在這個自定義的活動中,重寫CacheMetadata方法,實現當服務中沒有此擴展就添加此擴展。

代碼
 
public sealed class GetAverage : CodeActivity<string>
{
public InArgument<int> Number { get; set; }


protected override void CacheMetadata(CodeActivityMetadata metadata)
{

// This activity requires the average extension
metadata.RequireExtension(typeof (AverageExtension));

// This lambda will create one if it is not already added
metadata.AddDefaultExtensionProvider(() => new AverageExtension());

base.CacheMetadata(metadata);
}


protected override string Execute(CodeActivityContext context)
{

// Get the average extension
var average = context.GetExtension<AverageExtension>();

if (average == null)
throw new InvalidOperationException("Cannot access AverageExtension");

var number
= Number.Get(context);

// Store this number
average.StoreNumber(number);

return string.Format("Stored {0}, Average:{1}", number, average.GetAverage());
}
}

  工作流服務如下:

  如何測試:

  1、添加WorkflowTestHelper引用。

  2、配置測試環境。

  3、為WorkflowService添加DeploymentItem屬性標簽。

  4、使用WorkflowServiceTestHost宿主測試程序,代碼如下:

代碼
 
private readonly Binding _binding = new NetNamedPipeBinding();

/// <summary>
/// The endpoint address to be used by the test host
/// </summary>
private readonly EndpointAddress _serviceAddress = new EndpointAddress("net.pipe://localhost/TestService");


/// <summary>
/// Tests the GetAverage Activity by invoking it in a WorkflowService multiple times
/// </summary>
[TestMethod()]
[DeploymentItem(
"Service1.xamlx")]
public void ShouldInvokeAverageExtension()
{

const string expected1 = "Stored 33, Average:33";
const string expected2 = "Stored 44, Average:38.5";
const string expected3 = "Stored 55, Average:44";

string result1;
string result2;
string result3;

// Self-Host Service1.xamlx using Named Pipes
using (var testHost = WorkflowServiceTestHost.Open("Service1.xamlx", _serviceAddress.Uri))
{

// Use the generated proxy with named pipes
var proxy = new ServiceClient(_binding, _serviceAddress);

try
{
result1
= proxy.GetData(33);
result2
= proxy.GetData(44);
result3
= proxy.GetData(55);
proxy.Close();
}

catch (Exception)
{
proxy.Abort();

throw;
}

Assert.AreEqual(expected1, result1);
Assert.AreEqual(expected2, result2);
Assert.AreEqual(expected3, result3);
}
}

   總結:這篇文章是對WF4.0單元測試的一個簡單介紹。WorklowTestHelper是一個MS的老外為WF4.0單元測試寫的一個類庫。測試類庫和測試的例子http://code.msdn.microsoft.com/wfth/中都有下載。

   參考http://blogs.msdn.com/b/rjacobs/archive/2010/09/13/how-to-unit-test-a-workflowservice.aspx

0
0
 
標簽:WF4 單元測試
 
 

文章列表

全站熱搜
創作者介紹
創作者 大師兄 的頭像
大師兄

IT工程師數位筆記本

大師兄 發表在 痞客邦 留言(0) 人氣()