I'm unit testing some asynchronous code. I have tried to abstract it to make the issue more clear. My issue is that I want to set up the mocked Bar to execute Foo's private callback method after BeginWork returns. The callback is supposed to call Set() on the ManualResetEvent allowing the calling thread to continue to run. When I run the test my thread blocks indefinitely at the call to WaitOne().
Code under test:
using System.Threading;
using NUnit.Framework;
using Moq;
using System.Reflection;
public interface IBar
{
int BeginWork(AsyncCallback callback);
}
public class Bar : IBar
{
public int BeginWork(AsyncCallback callback)
{
// do stuff
} // execute callback
}
public class Foo
{
public static ManualResetEvent workDone = new ManualResetEvent(false);
private IBar bar;
public Foo(IBar bar)
{
this.bar = bar;
}
public bool DoWork()
{
bar.BeginWork(new AsyncCallback(DoWorkCallback));
workDone.WaitOne(); // thread blocks here
return true;
}
private void DoWorkCallback(int valueFromBeginWork)
{
workDone.Set();
}
}
Test Code:
[Test]
public void Test()
{
Mock<IBar> mockBar = new Mock<IBar>(MockBehavior.Strict);
// get private callback
MethodInfo callback = typeof(Foo).GetMethod("DoWorkCallback",
BindingFlags.Instance | BindingFlags.NonPublic);
mockBar.Setup(() => BeginWork(It.IsAny<AsyncCallback>()))
.Returns(0).Callback(() => callback.Invoke(0));
Foo = new Foo(mockBar.Object);
Assert.That(Foo.DoWork());
}
First observation was that you pass in a mocked
ISocketinstateand try to cast it toSocketin async callback which will result in a null error which meansconnectDone.Set()is never called soWaitOnewill not unblock.Change that to
Second observation was that you were not setting up the mocked calls correctly. No need for reflection here as you needed to get the passed arguments from the mock and invoke then in the mock callback setup
The following is based on your original code. Review it to get an understanding of what was explained above.
Finally I believe you should consider changing this code to use TPL to get around the whole call back and
IAsyncResultdrama. Basically exposing an async API and wrapping the calls with aTaskCompletionSource<T>but I guess that is outside of the scope of this question.