8

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

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

我正在使用 NUnit 和起订量。

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";
     }  
}
4

3 回答 3

9

抽象您的代码,以便您可以在测试时注入您想要的行为。

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

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

于 2011-05-04T15:49:07.717 回答
2

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

IAsynchResult ar = action.BeginInvoke(...);
ar.AsyncWaitHandle.WaitOne();
于 2011-05-04T15:43:11.957 回答
0

您无需检查是否调用了方法,而是测试最终结果 - 在本例中为 _view.Propert1 == "xyz"。

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

您可能会考虑取消操作 (PopulateView) 以跳过睡眠。

于 2011-05-04T15:43:25.170 回答