Question

Voici un pour les accros de filetage là-bas. J'ai cette méthode:

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

        });

    }

Il est défini dans un contrôle utilisateur WPF. MeltsAvailable est un ObservableCollection de MeltDtos. Ce code fonctionne à merveille lors de l'exécution dans l'application elle-même.

Le problème est que je voudrais créer un test unitaire, en utilisant NMock, pour vérifier les résultats de cette méthode - en particulier, qu'une fois qu'il est appelé, la propriété MeltsAvailable a quelques éléments. Voici la méthode d'essai:

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

    }

Le test échoue toujours sur la première Assert.AreEqual. vm.MeltsAvailable est vide à ce point.

Si je bande tout le filetage et le laisser comme:

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

Le test passe.

Alors, évidemment, il y a quelque chose qu'il ne aime pas sur les fils - mais même tourner sur Debug-> exceptions près> CLR exceptions près> Jeté et éteindre juste mon code, je reçois aucune exception du tout RefreshMelts.

La partie est que plus étrange l'appel Dispatcher.Invoke où je charge les objets MeltDto dans la collection MeltsAvailable ne semble jamais être appelé. Je peux Blanket toute la section avec des points d'arrêt, et ils obtiennent jamais frapper. Stimuler le temps Thread.Sleep dans mon test, même aussi haut que dix secondes ne change rien.

Pourquoi? Pourquoi cette section n'exécutait, pourquoi je ne peux pas en elle ou en pause, pourquoi je ne reçois pas des exceptions, pourquoi il fonctionne bien dans l'exécution mais pas dans un test?

Merci beaucoup, Steve

Était-ce utile?

La solution

Le répartiteur est une boucle de message qui est lié au fil d'exécution. Il traite les articles dans sa file d'attente lorsque le fil principal est inactif. Dans un test unitaire, qui ne se produit jamais. Le fil est occupé et il sort quand le test est terminé.

Si vous utilisez Visual Studio pour exécuter vos tests, vous pouvez activer la couverture de code mettant en surbrillance et vous verrez que le code à l'intérieur Dispatcher.Invoke () est jamais appelé (il sera affiché en rouge).

A DispatcherFrame peut être utilisé pour déclencher le régulateur pour traiter les messages en file d'attente. Ajoutez la classe d'aide suivante à votre projet de test unitaire:

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

A la fin de votre test (avant les assertions) DispatcherHelper.DoEvents d'appel (). Cela déclenche le Dispatcher pour traiter les événements exceptionnels, tels que ceux qui ajoutent des éléments à la collection observable du modèle de vue. Vous pouvez ensuite vérifier les propriétés du modèle afin de vérifier qu'ils ont été correctement définies.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top