Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to setup a BeginXXX EndXXX method call with moq?

Let's say I have some APM (BeginXxx, EndXxx) pattern async methods (as part of some WCF service proxy i'm calling):

public interface ISomeService
{
    IAsyncResult BeginSomeMethod(int num, AsyncCallback callback, object state);
    int EndSomeMethod(IAsyncResult ar);
}

My actual code uses uses the Task.Factory.FromAsync to create a Task, and then awaiting this task using the new async/await pattern introduced in .net 4.5.

I would like to test my class and thus I need to write a method that receives the mock, begin method, end method and return value and sets up the mock so that it would eventually return the required return value.

example usage:

SetupAsync(mock, mocked => mocked.BeginSomeMethod, mocked=> mocked.EndSomeMethod, 7);

Which will cause an async flow with any int argument to return 7. I cannot seem to figure out how to accomplish such a thing in moq.

like image 268
rony l Avatar asked Jun 30 '12 11:06

rony l


1 Answers

First, I recommend that you use the TaskWsdlImportExtension to create Task-based asynchronous WCF proxies. VS2012 does this by default, but you have to set it up yourself on VS2010+AsyncCTP. It's easier to unit test against a Task API.

If you do want to unit test against Begin/End, I think the easiest way would be to use Task-based mocks and expose Begin/End endpoints. The [AsyncFactory|AsyncFactory<T>].[ToBegin|ToEnd] methods in my AsyncEx library provide Begin/End method wrappers around a Task, or you can see Stephen Toub's blog post about writing these kinds of wrappers.

You can get simple already-completed tasks from Task.FromResult, or you can use the following construct to force an asynchronously-completed task:

Task.Run(async () => { await Task.Yield(); return 7; });

(the Async CTP equivalent would be):

TaskEx.RunEx(async () => { await TaskEx.Yield(); return 7; });

I'm not entirely sure how to tie this into Moq. Again, I suspect a Task-based API would be easier to mock than Begin/End. Begin/End has its own special semantics (you have to pass the correct IAsyncResult, End must be called exactly once for each Begin, etc), so there's more stuff to test.

like image 138
Stephen Cleary Avatar answered Nov 16 '22 00:11

Stephen Cleary