【问题标题】:Testing an asynchronous WCF service using MSTest使用 MSTest 测试异步 WCF 服务
【发布时间】:2015-07-15 22:43:53
【问题描述】:

我已经使用 C# 中的 async\await 方法实现了 WCF 服务。服务运营合同如下:

[OperationContract]
Task<ResponseMessage<ProcessMultipleTransactionsResponse>> ProcessMultipleTransactionsAsync(RequestMessage<ProcessMultipleTransactionsRequest> msgIn);

实现的方法签名是:

public async Task<ResponseMessage<ProcessMultipleTransactionsResponse>> ProcessMultipleTransactionsAsync(RequestMessage<ProcessMultipleTransactionsRequest> msgIn)

我正在尝试使用 VS 2012 中的单元测试来测试此服务。构建代理后,我使用以下方法:

var actual = await target.ProcessMultipleTransactionsAsync(request);

测试方法有 async 关键字,返回类型为 Task。执行测试时出现以下错误:

测试方法 Services.Implementations.UnitTests.CoreBanking.CoreBankingServiceTest.ProcessMultipleTransactions_JMD_JMD_ACHTransactions_Test 抛出异常: System.ArgumentException:向“End”方法提供了不正确的 IAsyncResult。传递给“End”的 IAsyncResult 对象必须是从匹配的“Begin”返回的对象,或者传递给提供给“Begin”的回调的对象。 参数名称:结果

结果堆栈跟踪:

at System.Runtime.AsyncResult.End[TAsyncResult](IAsyncResult result)
at System.ServiceModel.Channels.ServiceChannel.SendAsyncResult.End(SendAsyncResult result)
at System.ServiceModel.Channels.ServiceChannel.EndCall(String action, Object[] outs, IAsyncResult result)
at System.ServiceModel.Channels.ServiceChannelProxy.TaskCreator.<>c__DisplayClass2`1.<CreateGenericTask>b__3(IAsyncResult asyncResult)
at System.Threading.Tasks.TaskFactory`1.FromAsyncCoreLogic(IAsyncResult iar, Func`2 endFunction, Action`1 endAction, Task`1 promise, Boolean requiresSynchronization)
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
at Services.Implementations.UnitTests.CoreBanking.CoreBankingServiceTest.<ProcessMultipleTransactions_JMD_JMD_ACHTransactions_Test>d__10.MoveNext() in c:\...\CoreBankingServiceTest.cs:line 5716
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.GetResult()

任何想法可能导致这种情况?

【问题讨论】:

    标签: c# .net wcf async-await mstest


    【解决方案1】:

    如果您尝试测试服务,我认为您应该避免完全测试代理代码和 WCF 访问 - 它们给测试增加了不必要的复杂性,这意味着您测试的内容比您预期的要多得多到。只需在您的测试方法或测试初始化​​方法中构建您的服务,并避免 WCF 与绑定等的所有复杂性。

    如果出于某种原因,您想在此测试中通过 WCF 调用您的服务 - 更多的是集成测试,那么您将不得不使用适当的绑定自托管它。一旦测试初始化​​创建了准备好测试调用它的服务,我的第一个建议是仍然放弃代理并使用System.ServiceModel.ChannelFactory&lt;&gt;,因此您正在针对服务正在实现的完全相同的接口构建客户端代码(您不要'不必记住在服务合同发生更改时重新生成您的代理)。它是这样的(此代码使用与端点相同的 app.config 配置):

    var factory = new ChannelFactory<IService>("NameOfIServiceEndpointDeclaredInConfig");    
    var target = factory.CreateChannel();
    //Now allowing you to do the following, without the complication of the old-style proxy code
    var actual = await target.ProcessMultipleTransactionsAsync(request);
    

    使用此代码,目标将被键入为 IService,而不是某些 ServiceProxy 类。我希望这能消除所有关于 Begin 和 End 以及 IAsyncResult 的垃圾——这些都是在 .NET 1.0 中首次引入的旧异步编程模型的一部分。

    您已经在操作合同中使用 .NET 4 的 TAP - 但是我不知道 Task 可以安全地通过哪种协议 - 例如,我非常怀疑您是否可以通过 WSHttpBinding 来传递它。因此,您需要考虑您的服务以及如何称呼它。也许对于单元测试,命名管道可能会起作用(我不知道,也许其他人可以发表评论)。但正常情况是什么?通过网络?在这种情况下,最好放弃 Task 返回并使其成为同步方法,然后使用异步方法生成代理,然后使用 FromAsync 扩展方法将该 APM 约定转换为 TAP 约定,然后您可以在你的测试中等待。或者更好,因为我只是建议放弃代理,异步调用同步方法,例如:

    var actual = await Task.Run(() => target.ProcessMultipleTransactions(request));
    

    【讨论】:

    • 感谢 Sam,事实证明我们用来构建 WCF 代理的框架是问题的根源。当我尝试使用 ChannelFactory 不使用该框架时,我能够异步调用服务。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-01-04
    • 1970-01-01
    • 1970-01-01
    • 2012-10-03
    • 2011-05-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多