我正在寻找一种在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";
}
}
答案 0 :(得分: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);
在运行时,它是异步的。在测试时,它会阻止呼叫。
答案 1 :(得分:2)
BeginInvoke()
会返回您可以用来等待的IAsyncResult
。
IAsynchResult ar = action.BeginInvoke(...);
ar.AsyncWaitHandle.WaitOne();
答案 2 :(得分:0)
您无需检查是否调用了方法而是测试最终结果 - 在这种情况下_view.Propert1 ==“xyz”。
因为它是一个异步调用,你可能需要有一个循环,定期断言已设置该值,测试或检查的超时也是必须的,否则你的测试永远不会失败(只是卡住)。
您可以考虑删除操作(PopulateView)以跳过睡眠。