在C#中的异步委托调用单元测试

问题描述:

我有创建了一个委托和对象上开始的BeginInvoke,与其他功能传递在等待EndInvoke会的功能:在C#中的异步委托调用单元测试

private static void DeploymentComponentThreadedCallBack(IAsyncResult ar) 
    { 
     var result = (AsyncResult)ar; 
     var pluginExecuteAction = (Action<int, Guid, int, EnvironmentServerComponentSet, string>)result.AsyncDelegate; 
     pluginExecuteAction.EndInvoke(ar); 
     //report back to WCF service that thread is finished 
    } 

public void DeployComponent(byte[] resource, Guid componentGuid, string deploymentType, Dictionary<string, object> args) 
{ 
    var asyncCallback = new AsyncCallback(DeploymentComponentThreadedCallBack); 
    IDeployComponent plugin = GetPluginDelegate(); 
    Action<byte[], Guid, string, Dictionary<string, object>> pluginExecuteAction = plugin.DeployComponent; 
    IAsyncResult ar = pluginExecuteAction.BeginInvoke(resource, componentGuid, deploymentType, args, asyncCallback, null); 
} 

我想单元测试这个,但是当我这样做的时候,DeploymentComponentThreadedCallBack永远不会被触发,显然,EndInvoke调用也不会。我认为这是因为在异步线程结束之前测试通过,因此线程在EndInvoke之前停止执行,但是有没有办法阻止这种情况的发生,以便我可以看到EndInvoke被命中?

干杯, 马特

我认为你的基本问题是你没有公开DeployComponent方法中的任何东西,它可以让你跟踪你从那里开始的异步操作。如果您从那里返回IAsyncResult,则可以致电ar.AsyncWaitHandle.WaitOne()等待完成。

+0

不幸的是,这是示例代码 - 实际上,DeployComponent除此之外还有更多的东西,它已经有了一个bool返回类型。 (还是)感谢你的建议。 –

+0

那么你必须以某种方式解决这个问题。如果你想等待一个操作完成,你必须公开一些东西给调用者等待。在正常情况下,你继续,不要等待完成,但在你的测试中,你等待它完成。实际上,只需将DeployComponent更改为遵循APM并在所有启动完成时报告完成。它可能需要一些重构。 – RandomEngy

至于我记得AsyncResult有一个标志(IsCompleted),告诉您该操作是否是怎么回事。等待它(例如用一个while循环初始化),然后做你的断言

+0

有这样一个标志,但是我如何在调用对象中得到它?即如果DeployComponent方法在Foo对象上,并且在我的测试中,我调用Foo.DeployComponent(byteStream,new Guid(),“cmd”,null);我在哪里得到AsyncResult,看看它是否完成? –

+0

我无法看到你来自哪里。您将AsyncResult传递给第一个方法,因此您可以在传入之前等待它。 – flq

+0

啊我明白你的意思了。这不会起作用,因为在正常情况下,我不想等待它 - 仅用于此测试。否则,不会异步执行该操作。 –

你只需要做一个注入点来将异步调用变成阻塞调用。例如:

public class MethodInvoker 
{ 
    public virtual void Invoke(Action begin, Action<IAsyncResult> end) 
    { 
      begin.BeginInvoke(end, null); 
    } 
} 

有了一个单元测试的版本,像这样:

public class SynchronousInvoker : MethodInvoker 
{ 
    public override void Invoke(Action begin, Action<IAsyncResult> end) 
    { 
     begin(); 
     end(); 
    } 
} 

然后你会写代码,像这样:

_myMethodInvoker.Invoke(pluginExecuteAction, asyncCallback); 

这在你的正常功能的情况下,是异步。在你的单元测试中,你只需将SynchronousInvoker注入到它的位置,它就成为一个阻塞调用。

+0

这看起来很不错,但我无法调整它使其工作。 BeginInvoke需要AsyncResult类型的参数,而不是Action ,如果没有Action,则不能调用它的end()。 –