2011-01-06 4 views
2

Вот один из тех, кто занимается разметкой. У меня есть этот метод:Испытание на выпуск NMock против WPF и диспетчера

public void RefreshMelts() 
    { 
     MeltsAvailable.Clear(); 

     ThreadPool.QueueUserWorkItem(delegate 
     { 
      Dispatcher.BeginInvoke((ThreadStart)delegate 
      { 
       eventAggregator.GetEvent<BusyEvent>().Publish(true); 
       eventAggregator.GetEvent<StatusMessageEvent>().Publish(
        new StatusMessage("Loading melts...", MessageSeverity.Low)); 
      }); 

      try 
      { 
       IList<MeltDto> meltDtos = meltingAppService.GetActiveMelts(); 

       Dispatcher.Invoke((ThreadStart)delegate 
       { 
        foreach (MeltDto availableMelt in meltDtos) 
        { 
         MeltsAvailable.Add(availableMelt); 
        } 
        OnPropertyChanged("MeltsAvailable"); 

        eventAggregator.GetEvent<BusyEvent>().Publish(false); 
        eventAggregator.GetEvent<StatusMessageEvent>().Publish(
         new StatusMessage("Melts loaded", MessageSeverity.Low)); 
       }); 
      } 
      catch (ApplicationException ex) 
      { 
       log.Error("An error occurred in MeltsViewModel when attempting to load melts", ex); 

       Dispatcher.Invoke((ThreadStart)delegate 
       { 
        MeltsAvailable.Clear(); 

        eventAggregator.GetEvent<StatusMessageEvent>().Publish(
         new StatusMessage("Melt data could not be loaded because an error occurred; " + 
          "see the application log for detail", 
          MessageSeverity.High)); 
        eventAggregator.GetEvent<BusyEvent>().Publish(false); 
       }); 
      } 

     }); 

    } 

Это определено в пользовательском элементе управления WPF. MeltsAvailable - ObservableCollection of MeltDtos. Этот код прекрасно работает при работе в самом приложении.

Проблема заключается в том, что я хотел бы создать модульный тест, используя NMock, для проверки результатов этого метода - в частности, что после его вызова у свойства MeltsAvailable есть некоторые элементы. Ниже приведено тестовое испытание:

[TestMethod] 
    public void GetAvailableMeltsTest() 
    { 
     MeltDto mockMelt1 = new MeltDto(); 
     MeltDto mockMelt2 = new MeltDto(); 

     mockMelt1.MeltIdentifier = "TST0001"; 
     mockMelt2.MeltIdentifier = "TST0002"; 

     IList<MeltDto> availableMelts = new List<MeltDto>(); 
     availableMelts.Add(mockMelt1); 
     availableMelts.Add(mockMelt2); 

     Expect.Exactly(1).On(service).Method("GetActiveMelts").Will(Return.Value(availableMelts)); 


     MeltsViewModel vm = new MeltsViewModel(aggregator, logger, service, configManagerFactory); // All of these are mock objects 

     vm.RefreshMelts(); 
     Thread.Sleep(millisecondDelayForEventPublish * 100); 

     mockery.VerifyAllExpectationsHaveBeenMet(); 

     Assert.AreEqual(vm.MeltsAvailable.Count, 2); 
     Assert.AreEqual(vm.MeltsAvailable[0].MeltIdentifier, "TST0001"); 
     Assert.AreEqual(vm.MeltsAvailable[1].MeltIdentifier, "TST0002"); 

    } 

Тест последовательно не срабатывает при первом утверждении.AreEqual. vm.MeltsAvailable пуст в этой точке.

Если я вырезать всю резьбу и оставить его так же, как:

public void RefreshMelts() 
    { 
     MeltsAvailable.Clear(); 
     IList<MeltDto> meltDtos = meltingAppService.GetActiveMelts(); 
     foreach (MeltDto availableMelt in meltDtos) 
     { 
      MeltsAvailable.Add(availableMelt); 
     } 
     OnPropertyChanged("MeltsAvailable"); 
    } 

тест проходит.

Так что, очевидно, что-то, что ему не нравится в потоках, но даже включив Debug-> Exceptions-> CLR Exceptions-> Thrown и отключив Just My Code, я не получаю никаких исключений вообще в RefreshMelts ,

Самая странная часть заключается в том, что вызов Dispatcher.Invoke, где я загружаю объекты MeltDto в коллекцию MeltsAvailable, никогда не называется. Я могу покрыть весь раздел точками останова, и они никогда не попадают. Увеличение скорости Thread.Sleep в моем тесте даже до десяти секунд ничего не меняет.

Почему? Почему этот раздел не выполняется, почему я не могу войти в него или не вломиться в него, почему я не получаю исключения, почему он отлично работает при выполнении, но не в тесте?

Спасибо большое, Стива

ответ

4

Диспетчер представляет собой цикл обработки сообщений, который привязан к выполняющемуся потоку. Он обрабатывает элементы в очереди, когда основной поток неактивен. В единичном тесте это никогда не происходит. Поток занят, а затем он завершается, когда тест завершен.

Если вы используете Visual Studio для запуска тестов, вы можете включить подсветку кода, и вы увидите, что этот код внутри Dispatcher.Invoke() никогда не вызывается (он будет отображаться красным цветом).

DispatcherFrame может использоваться для запуска диспетчера для обработки сообщений в очереди. Добавьте следующий вспомогательный класс для тестового проекта блока:

public static class DispatcherHelper 
{ 
    public static void DoEvents() 
    { 
     DispatcherFrame frame = new DispatcherFrame(); 
     Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background, new DispatcherOperationCallback(ExitFrame), frame); 
     Dispatcher.PushFrame(frame); 
    } 

    private static object ExitFrame(object frame) 
    { 
     ((DispatcherFrame)frame).Continue = false; 
     return null; 
    } 
} 

В конце теста (до утверждения) DispatcherHelper.DoEvents вызова(). Это заставит Диспетчер обрабатывать выдающиеся события, такие как те, которые добавляют элементы в наблюдаемую коллекцию модели представления. Затем вы можете проверить свойства модели представления, чтобы убедиться, что они были установлены правильно.

Смежные вопросы