2011-02-20 8 views
7

[Unidad de prueba del novato] [C#]método que imita la acción <T> parámetro

en cuenta la situación siguiente:

estoy usando Silverlight y llamar a un servicio WCF. Silverlight solo puede llamar a los servicios de WCF de forma asincrónica. Construyo un contenedor alrededor del servicio WCF para que pueda trabajar con los parámetros de Acción. (hace que el código del cliente sea mucho más limpio).

Así que tengo un servicio asíncrono que recupera salas de reuniones.

public interface IMeetingRoomService 
{ 
    void GetRooms(Action<List<MeetingRoom>> result); 
} 

GetRooms convirtiendo en List<MeetingRoom> GetRooms() no es una opción.

Quiero utilizar este servicio en un ViewModel para establecer una propiedad pública llamada Salas.

public class SomeViewModel 
{ 
    private readonly IMeetingRoomService _meetingRoomService; 

    public List<MeetingRoom> Rooms { get; set; } 

    public SomeViewModel(IMeetingRoomService meetingRoomService) 
    { 
     this._meetingRoomService = meetingRoomService; 
    } 

    public void GetRooms() 
    { 
     // Code that calls the service and sets this.Rooms 
     _meetingRoomService.GetRooms(result => Rooms = result); 
    } 
} 

Quiero probar por unidad la implementación de SomeViewModel.GetRooms(). (Para esta pregunta, escribí rápidamente la implementación, pero en realidad estoy tratando de usar TDD.)

¿Cómo termino esta prueba? Estoy usando NUnit y Moq.

[Test] 
public void GetRooms_ShouldSetRooms() 
{ 
    var theRooms = new List<MeetingRoom> 
         { 
          new MeetingRoom(1, "some room"), 
          new MeetingRoom(2, "some other room"), 
         }; 

    var meetingRoomService = new Mock<IMeetingRoomService>(); 

    //How do I setup meetingRoomService so that it gives theRooms in the Action?? 


    var viewModel = new SomeViewModel(meetingRoomService.Object); 

    viewModel.GetRooms(); 

    Assert.AreEqual(theRooms, viewModel .Rooms); 
} 

EDIT:

Solución

respuesta de Lee Stephane primero.

Este es el Código de ensayo terminé escribiendo gracias a la respuesta de Stéphane:

[Test] 
public void GetRooms_ShouldSetRooms() 
{ 
    var meetingRoomService = new Mock<IMeetingRoomService>(); 
    var shell = new ShellViewModel(meetingRoomService.Object); 
    var theRooms = new List<MeetingRoom> 
         { 
          new MeetingRoom(1, "some room"), 
          new MeetingRoom(2, "some other room"), 
         }; 

    meetingRoomService 
     .Setup(service => service.GetRooms(It.IsAny<Action<List<MeetingRoom>>>())) 
     .Callback((Action<List<MeetingRoom>> action) => action(theRooms)); 

    shell.GetRooms(); 

    Assert.AreEqual(theRooms, shell.Rooms); 
} 
+0

que volvería una tarea '' CodesInChaos

+0

ehhh ..... qué? –

Respuesta

8

Aquí hay un código de pseudo, no he t ejecutarlo. Pero creo que eso es lo que quieres.

SetupCallback es lo que le interesa.

Para todas las llamadas a _meetingRoomServiceFake.GetRooms, basta con establecer el _getRoomsCallback al parámetro se ha pasado.

Ahora tiene una referencia a la devolución de llamada que son pasando su modelo de vista, y puede llamarlo con la lista de salas de reuniones que desee probar. Para que pueda probar su código asíncrono casi de la misma manera que el código sincrónico.es solo un poco más ceremonial configurar el falso.

Action<List<MeetingRoom>> _getRoomsCallback = null; 
IMeetingRoomService _meetingRoomServiceFake; 


private void SetupCallback() 
{ 
    Mock.Get(_meetingRoomServiceFake) 
     .Setup(f => f.GetRooms(It.IsAny<Action<List<MeetingRoom>>>())) 
     .Callback((Action<List<MeetingRoom>> cb) => _getRoomsCallback= cb); 
} 

[Setup] 
public void Setup() 
{ 
    _meetingRoomServiceFake = Mock.Of<IMeetingRoomService>(); 
    SetupCallback(); 
} 

[Test] 
public void Test() 
{ 

     var viewModel = new SomeViewModel(_meetingRoomServiceFake) 

     //in there the mock gets called and sets the _getRoomsCallback field. 
     viewModel.GetRooms(); 
     var theRooms = new List<MeetingRoom> 
        { 
         new MeetingRoom(1, "some room"), 
         new MeetingRoom(2, "some other room"), 
        }; 

    //this will call whatever was passed as callback in your viewModel. 
    _getRoomsCallback(theRooms); 
} 
+1

Voy a intentar esto y dar retroalimentación –

+0

Increíble, ¡funciona! No lo entiendo completamente, pero funciona. Tuve que configurar _getRoomsCallBack como nulo o el código no se compilaría. Y para su información sin implementación, esta prueba arrojó una excepción de nullreference en esta línea: "getRoomsCallback (theRooms);" porque la devolución de llamada todavía era nula –

+0

Añadiré detalles para explicar :) –

0

Se podría utilizar un AutoResetEvent para manejar las llamadas asíncronas.

Simplemente inícielo como desarmado y configure su servicio simulado para configurarlo en la devolución de llamada. (IE: var = new mockService Mock(); mockService.SetUp (x => x.MyMethod()) Devuelve (someStuff) .Callback (() => handle.Set()); .)

Después de eso uso hadle.WaitOne (1000) para comprobar si se llamó. (En mi humilde opinión 1000 milisegundos son más que suficientes para ejecutar el código asincrónico).

Lo sentimos: Se suponía que esto vaya como una respuesta al post anterior ... No puedo por la vida de a averiguar cómo responder :)

Cuestiones relacionadas