Domanda

Ecco uno per i drogati di filettatura là fuori. Ho questo metodo:

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

        });

    }

Questo è definita in un controllo utente WPF. MeltsAvailable è un ObservableCollection di MeltDtos. Questo codice funziona a meraviglia quando si esegue l'applicazione stessa.

Il problema è che vorrei creare un test di unità, utilizzando NMock, per verificare i risultati di questo metodo - in particolare, che una volta si chiama, la proprietà MeltsAvailable ha alcuni elementi. Ecco il metodo di prova:

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

    }

Il test fallisce costantemente al primo Assert.AreEqual. vm.MeltsAvailable è vuota in quel punto.

Se la spoglierò fuori tutto il threading e lo lascio come:

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

Il test viene superato.

Così, ovviamente, c'è qualcosa che non piace circa le discussioni -, ma anche di accendere Debug-> eccezioni-> CLR eccezioni-> Gettato, e spegnendo Just My Code, ottengo senza eccezioni a tutti in RefreshMelts.

La parte più strana è che la chiamata Dispatcher.Invoke in cui si carica il MeltDto oggetti nella collezione MeltsAvailable non sembra mai essere chiamato. Posso ricoprire l'intera sezione con i punti di interruzione, e non hanno mai ottenere successo. Aumentare il tempo di Thread.Sleep nel mio test di addirittura il secondo dieci non cambia nulla.

Perché? Perché quella sezione non in esecuzione, perché riesco a passo non ho in esso o per una pausa in esso, perché non ricevo eccezioni, perché funziona bene in esecuzione, ma non in un test?

Grazie molto, Steve

È stato utile?

Soluzione

Il dispatcher è un loop di messaggi che è legato al thread di esecuzione. Elabora le voci nella coda quando il filo principale è inattivo. In un test di unità, che non succede. Il filo è occupato e poi esce quando il test è stato completato.

Se si sta utilizzando Visual Studio per eseguire i test, è possibile attivare la copertura del codice evidenziando e vedrete che il codice all'interno Dispatcher.Invoke () non viene mai chiamato (verrà visualizzato in rosso).

Un DispatcherFrame può essere utilizzato per attivare il Dispatcher al processo in coda i messaggi. Aggiungere la seguente classe di supporto al progetto unit test:

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

Al termine del test (prima delle affermazioni) DispatcherHelper.DoEvents di chiamata (). Ciò attiverà il Dispatcher per elaborare gli eventi eccezionali, come ad esempio quelli che aggiungono elementi da collezione osservabili del modello di vista. È quindi possibile controllare le proprietà del modello al fine di verificare che siano stati impostati correttamente.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top