2011-02-20 14 views
7

[unit testing newbie] [C#]metodo di derisione con azione <T> parametro

Si consideri il seguente scenario:

sto utilizzando Silverlight e chiamare un servizio WCF. Silverlight può solo chiamare servizi WCF in modo asincrono. Costruisco un wrapper attorno al servizio WCF in modo che possa lavorare con i parametri Action. (rende il codice cliente molto più pulito).

Quindi ho un servizio asincrono che recupera le sale riunioni.

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

tornitura GetRooms in List<MeetingRoom> GetRooms() non è un'opzione.

Desidero utilizzare questo servizio in un ViewModel per impostare una proprietà pubblica denominata Camere.

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

Voglio testare l'implementazione di SomeViewModel.GetRooms(). (Per questa domanda ho scritto rapidamente l'implementazione ma in realtà sto cercando di usare TDD.)

Come posso finire questo test? Sto usando NUnit e 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:

Soluzione

risposta Leggi Stephane di prima.

Questo è il codice di prova ho finito per scrivere grazie alla risposta di 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

mi piacerebbe tornare un '' task CodesInChaos

+0

ehhh ..... cosa? –

risposta

8

Ecco alcuni pseudo codice, mi rifugio' t eseguilo. Ma penso che sia quello che vuoi.

SetupCallback è quello che interessa.

Per tutte le chiamate a _meetingRoomServiceFake.GetRooms, è sufficiente impostare il _getRoomsCallback al parametro passato in.

Si dispone ora di un riferimento alla funzione di callback che siete passando il tuo viewmodel, e puoi chiamarlo con qualsiasi lista di MeetingRooms che vuoi testarlo. Così puoi testare il tuo codice asincrono quasi allo stesso modo del codice sincrono.è solo un po 'più di cerimonia per impostare il 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

Proverò questo e dare un feedback –

+0

Incredibile, funziona! Non lo capisco completamente, ma funziona. Ho dovuto impostare _getRoomsCallBack su null o il codice non sarebbe compilato. E FYI senza implementazione questo test ha gettato un'eccezione nullreference su questa linea: "getRoomsCallback (theRooms);" perché il callback era ancora nullo –

+0

Aggiungerò i dettagli per spiegare :) –

0

È possibile usare un AutoResetEvent per gestire le chiamate asincrone.

Basta inizializzarlo come disinserito e configurare il servizio di simulazione per impostarlo nella richiamata. (IE: var mockService = new Mock(); mockService.SetUp (x => x.MyMethod()) restituisce (someStuff) .Callback (() => handle.Set()); .)

Successivamente, utilizzo hadle.WaitOne (1000) per verificare se è stato chiamato. (IMHO 1000 millisecondi sono più che sufficienti per eseguire il codice asincrono).

Siamo spiacenti: Questo doveva andare come una risposta al post di cui sopra ... non posso per la vita di me capire come rispondere :)

Problemi correlati