开发者

How to Unit Test BeginInvoke on an Action

开发者 https://www.devze.com 2023-03-02 15:51 出处:网络
I am looking for a way to test BeginInvoke on an Action method, since the method runs on a background thread there is no way of knowing when it actually completes or calls callback method. I am lookin

I am looking for a way to test BeginInvoke on an Action method, since the method runs on a background thread there is no way of knowing when it actually completes or calls callback method. I am looking for a way to keep my test wait until the callback gets called before making assertions.

In the following Presenter class, you can notice that I am invoking PopulateView on background thread which updates the view when data is fetched and I am trying assert the view Properties are correctly initialized.

I am using NUnit and Moq.

public class Presenter
{
    private IView _view;
    private IService _service;
    public Presenter(IView view, IService service)
    {
        _view = view;
        _service = service;

        Action action = PopulateView;  
        action.BeginInvoke(PopulateViewCallback, action);
    }
    private void PopulateViewCallback(IAsyncResult ar)
    {
            try
            {
                Action target = (Action)ar.AsyncState;
                ta开发者_Go百科rget.EndInvoke(ar);
            }
            catch (Exception ex)
            {
                Logger.Instance.LogException("Failed to initialize view", ex);
            }
    }

     private void PopulateView()
     {
          Thread.Sleep(2000); // Fetch data _service.DoSomeThing()
          _view.Property1 = "xyz";
     }  
}


Abstract your code so that you can inject the behavior you want at testing time.

public class MethodInvoker
{
    public virtual void InvokeMethod(Action method, Action callback)
    {
         method.BeginInvoke(callback, method);
    }
}

This version is asynchronous. At testing time, you can simply make a blocking version:

public class TestInvoker
{
    public IAsyncResult MockResult { get; set; }

    public override void InvokeMethod(Action method, Action callback)
    {
         method();
         callback(MockResult);
    }
}

Then your code simply changes to this:

 // Inject this dependency
Invoker.InvokeMethod(PopulateView, PopulateViewCallback);

At runtime, it's asynchronous. At testing time, it blocks the call.


BeginInvoke() returns an IAsyncResult which you can use to wait.

IAsynchResult ar = action.BeginInvoke(...);
ar.AsyncWaitHandle.WaitOne();


You don't need to check that methods are called instead test the end result - in this case that _view.Propert1 == "xyz".

Because it's an async call you might need to have a loop that Asserts periodically that the value has been set, also a timeout on the test or the check is a must otherwise your test would never fail (just stuck).

You might consider stubbing out the action (PopulateView) in order to skip the Sleep.

0

精彩评论

暂无评论...
验证码 换一张
取 消