使用MSTest测试异步WCF服务

时间:2015-05-06 01:14:36

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

我在C#中使用async \ _wait方法实现了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'的回调。 参数名称:结果

结果StackTrace:

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()

可能导致此问题的任何想法?

1 个答案:

答案 0 :(得分:1)

如果您正在尝试测试服务,我认为您应该避免完全测试代理代码和WCF访问 - 它们会给测试带来不必要的复杂性,这意味着您的测试要远远超过你打算。只需在测试方法或测试初始化​​方法中构建服务,并避免WCF的所有复杂性与绑定等等。

如果由于某种原因,您希望在此测试中通过WCF调用您的服务 - 更多的是集成测试,那么您将必须使用适当的绑定自行托管它。一旦测试初始化​​创建了准备测试它的服务,我的第一个建议是仍然放弃代理并改为使用System.ServiceModel.ChannelFactory<>,所以你要根据服务实现的完全相同的接口构建你的客户端代码(你在服务合同发生变更时,不必记住重新生成代理服务器。它是这样的(这段代码使用与端点相同的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);

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

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

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