Как выполнить юнит-тест BeginInvoke на действии - PullRequest
8 голосов
/ 04 мая 2011

Я ищу способ протестировать BeginInvoke в методе Action, поскольку метод работает в фоновом потоке, поэтому нет способа узнать, когда он фактически завершается или вызывает метод обратного вызова.Я ищу способ подождать, пока мой тест не будет вызван до обратного вызова.

В следующем классе 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";
     }  
}

Ответы [ 3 ]

9 голосов
/ 04 мая 2011

Абстрагируйте ваш код, чтобы вы могли ввести желаемое поведение во время тестирования

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

Во время выполнения он асинхронный.Во время тестирования он блокирует вызов.

2 голосов
/ 04 мая 2011

BeginInvoke() возвращает IAsyncResult, который можно использовать для ожидания.

IAsynchResult ar = action.BeginInvoke(...);
ar.AsyncWaitHandle.WaitOne();
0 голосов
/ 04 мая 2011

Вам не нужно проверять, что методы вызываются, вместо этого тестируйте конечный результат - в этом случае _view.Propert1 == "xyz".

Поскольку это асинхронный вызов, вам может понадобиться цикл, который периодически утверждает, что значение было установлено, а также время ожидания теста или проверка является обязательным, иначе ваш тест никогда не будет неудачным (просто застрял).

Возможно, вы захотите заглушить действие (PopulateView), чтобы пропустить Sleep.

...