Pregunta

Aquí está uno para los adictos de roscado por ahí. Tengo este método:

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

        });

    }

Esto se define en un control de usuario WPF. MeltsAvailable es un ObservableCollection de MeltDtos. Este código funciona muy bien cuando se ejecuta en la propia aplicación.

El problema es que me gustaría crear una unidad de prueba, utilizando NMock, para verificar los resultados de este método - en concreto, que una vez que se llama, la propiedad MeltsAvailable tiene algunos artículos. Aquí está el método de prueba:

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

    }

La prueba falla constantemente en el primer Assert.AreEqual. vm.MeltsAvailable está vacía en ese punto.

Si me tira toda la rosca y lo dejo tal como:

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

La prueba pasa.

Así que, obviamente, hay algo que no le gusta acerca de los temas - pero incluso encender Depuración> Excepciones-> CLR Excepciones-> Lanzado y apagar Sólo mi código, consigo ninguna excepción en absoluto en RefreshMelts.

Lo más extraño es que la llamada Dispatcher.Invoke donde me carga la MeltDto objetos en la colección MeltsAvailable nunca parece ser llamado. Puedo cobija toda la sección con puntos de interrupción, y nunca recibe un golpe. Impulsar el momento Thread.Sleep en mi prueba para incluso tan alto como diez segundos cambia nada.

¿Por qué? ¿Por qué es que la sección no ejecutando, qué puedo paso no me en ella o un descanso en él, ¿por qué no recibo excepciones, ¿por qué funciona bien en la ejecución, pero no en una prueba?

Gracias tanto, Steve

¿Fue útil?

Solución

El Dispatcher es un bucle de mensajes que está ligado al hilo de ejecución. Procesa los artículos en su cola cuando el hilo principal está inactivo. En una prueba de unidad, que nunca sucede. El hilo está ocupado y después de que salga cuando se haya completado la prueba.

Si está utilizando Visual Studio para ejecutar las pruebas, se puede activar la cobertura de código destacando y verá que el código dentro Dispatcher.Invoke () no se llama (se muestra en rojo).

A DispatcherFrame se puede utilizar para desencadenar la Dispatcher a proceso de mensajes en cola. Añadir la siguiente clase de ayuda a su proyecto de prueba de unidad:

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 final de la prueba (antes de las afirmaciones) DispatcherHelper.DoEvents de llamadas (). Esto hará que el despachador para procesar eventos pendientes, como los que se agregan elementos a la colección observables del modelo de vista. A continuación, puede inspeccionar las propiedades del modelo a fin de verificar que se han configurado correctamente.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top