如何对操作进行单元测试 BeginInvoke

发布于 2024-11-05 05:33:22 字数 1041 浏览 4 评论 0原文

我正在寻找一种在 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";
     }  
}

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

回梦 2024-11-12 05:33:22

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

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

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

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.

眼波传意 2024-11-12 05:33:22

BeginInvoke() 返回一个 IAsyncResult< /code>您可以使用它来等待。

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

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

IAsynchResult ar = action.BeginInvoke(...);
ar.AsyncWaitHandle.WaitOne();
黒涩兲箜 2024-11-12 05:33:22

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

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

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

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文