Antonne Tulloch
Antonne Tulloch

Reputation: 33

Testing an asynchronous WCF service using MSTest

I've implemented a WCF service using the async\await method in C#. The service operation contract is as follows:

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

The method signature for the implementation is:

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

I'm trying to test this service using a unit test in VS 2012. After building a proxy I consume the method using:

var actual = await target.ProcessMultipleTransactionsAsync(request);

The test method has the async keyword with a Task as the return type. When executing the test I'm getting the following error:

Test method Services.Implementations.UnitTests.CoreBanking.CoreBankingServiceTest.ProcessMultipleTransactions_JMD_JMD_ACHTransactions_Test threw exception: System.ArgumentException: An incorrect IAsyncResult was provided to an 'End' method. The IAsyncResult object passed to 'End' must be the one returned from the matching 'Begin' or passed to the callback provided to 'Begin'. Parameter name: result

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

Any ideas what may be causing this?

Upvotes: 2

Views: 1402

Answers (1)

Sam Nelson
Sam Nelson

Reputation: 387

If you are trying to test the service, I think you should avoid testing the proxy code and WCF access altogether - they add unnecessary complexity to the test meaning you're testing much more than you intended to. Just construct your service in your test method or test initialisation method, and avoid all the complication of WCF with bindings and so forth.

If for whatever reasons, you want to invoke your service via WCF within this test - more of an integration test, then you're going to have to self-host it with an appropriate binding. Once the test initialisation creates the service ready to test invoking it, my first suggestion is to still ditch the proxy and use a System.ServiceModel.ChannelFactory<> instead, so you are building your client code against the exact same interface the service is implementing (you don't have to remember to regen your proxy anytime a change is made to the service contract). It goes something like this (this code uses your same app.config configuration for endpoints):

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

with this code, target will be typed as IService, rather than some ServiceProxy class. I would expect that to cut out all the rubbish about Begin and End and IAsyncResult - that stuff is all part of the old Asynchronous Programming Model first introduced in .NET 1.0.

You're already using .NET 4's TAP in your operation contract - however I don't know which kind of protocols a Task could be safely passed across - I highly doubt you can pass it over an WSHttpBinding for example. So you need to think about your service and what is going to call it. Perhaps for a unit test, named pipes might work (I don't know, perhaps someone else can comment). But what is the normal case? Over the web? In that case it might be better to ditch the Task return and make it a synchronous method, but then generate a proxy with async methods, and then use the FromAsync extension method to convert that APM convention to a TAP one that you can then await in your test. Or better, since I just suggested ditching the proxy, invoke the synchronous method asynchronously with something like:

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

Upvotes: 1

Related Questions