2011-05-04 27 views
8

我正在尋找一種方法來測試Action方法上的BeginInvoke,因爲該方法在後臺線程上運行,所以無法知道它何時實際完成或調用回調方法。我正在尋找一種方法來保持我的測試等待,直到回調被調用之後才進行斷言。如何在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"; 
    } 
} 

回答

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

在運行時,它是異步的。在測試時間,它會阻止呼叫。

+0

聽起來不錯,已經考慮過它並可以將它用於新代碼,但是如何單元測試直接調用BeginInvoke的現有代碼。 – skjagini 2011-05-04 17:32:12

+1

重構?依賴存在於那裏;單元測試的唯一方法是將其抽象出來,或者不對其進行測試。 =/ – Tejs 2011-05-04 18:00:21

+0

'callback'參數必須是AsyncCallback類型。但是,謝謝你,這是偉大的:) – soniiic 2011-10-21 09:45:48

2

BeginInvoke()返回IAsyncResult,你可以用它來等待。

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

如何獲得IAsynchResult(ar)? – skjagini 2011-05-04 16:07:12

+0

它是BeginInvoke的返回值。你可以在代碼中看到。 – 2011-05-04 16:19:53

+0

我從構造函數調用它,並且沒有從類的外部訪問它來編寫單元測試。 – skjagini 2011-05-04 17:27:32

0

你並不需要檢查的方法被調用,而不是測試的最終結果 - 在這種情況下_view.Propert1 ==「XYZ」。

因爲這是一個異步調用,您可能需要一個定期斷言該值已設置的循環,測試或檢查的超時也是必須的,否則您的測試永遠不會失敗(只是卡住了)。

您可能會考慮刪除操作(PopulateView)以跳過睡眠。