WF4.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://www.cnblogs.com/zhuqil/archive/2010/10/09/how-to-unit-test-in-wf4.html,如需转载请自行联系原作者

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值