如何在Action上unit testingBeginInvoke

我正在寻找一种在Action方法上测试BeginInvoke的方法,因为该方法在后台线程上运行,无法知道它何时实际完成或调用回调方法。 我正在寻找一种方法来保持我的测试等待,直到在进行断言之前调用回调。

在下面的Presenter类中,您可以注意到我在后台线程上调用PopulateView,它在获取数据时更新视图,并且我正在尝试断言视图属性已正确初始化。

我正在使用NUnit和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; target.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"; } } 

摘要您的代码,以便您可以在测试时注入所需的行为。

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

这个版本是异步的。 在测试时,您可以简单地制作阻止版本:

 public class TestInvoker { public IAsyncResult MockResult { get; set; } public override void InvokeMethod(Action method, Action callback) { method(); callback(MockResult); } } 

然后您的代码只需更改为:

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

在运行时,它是异步的。 在测试时,它会阻止呼叫。

BeginInvoke()返回一个可以用来等待的IAsyncResult

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

您不需要检查是否调用了方法而是测试最终结果 – 在这种情况下,_view.Propert1 ==“xyz”。

因为它是一个异步调用,你可能需要有一个循环,定期断言已经设置了值,测试或检查的超时也是必须的,否则你的测试永远不会失败(只是卡住)。

您可以考虑删除操作(PopulateView)以跳过Sleep。