2009-11-19 12 views
27

Al momento ho:Come prova Ordina chiamata di metodo con Moq

[Test] 
    public void DrawDrawsAllScreensInTheReverseOrderOfTheStack() { 
     // Arrange. 
     var screenMockOne = new Mock<IScreen>(); 
     var screenMockTwo = new Mock<IScreen>(); 
     var screens = new List<IScreen>(); 
     screens.Add(screenMockOne.Object); 
     screens.Add(screenMockTwo.Object); 
     var stackOfScreensMock = new Mock<IScreenStack>(); 
     stackOfScreensMock.Setup(s => s.ToArray()).Returns(screens.ToArray()); 
     var screenManager = new ScreenManager(stackOfScreensMock.Object); 
     // Act. 
     screenManager.Draw(new Mock<GameTime>().Object); 
     // Assert. 
     screenMockOne.Verify(smo => smo.Draw(It.IsAny<GameTime>()), Times.Once(), 
      "Draw was not called on screen mock one"); 
     screenMockTwo.Verify(smo => smo.Draw(It.IsAny<GameTime>()), Times.Once(), 
      "Draw was not called on screen mock two"); 
    } 

Ma l'ordine in cui traggo i miei oggetti nel codice di produzione non ha importanza. Potrei farne una prima o due, non importa. Tuttavia dovrebbe essere importante in quanto l'ordine di estrazione è importante.

In che modo (utilizzando Moq) i metodi vengono richiamati in un determinato ordine?

Modifica

mi sono liberato di quel test. Il metodo di estrazione è stato rimosso dai miei test unitari. Dovrò solo testare manualmente funziona. L'inversione dell'ordine è stata però inserita in una classe di test separata in cui è stata testata, quindi non è affatto male.

Grazie per il collegamento sulla funzione che stanno esaminando. Spero davvero che venga aggiunto presto, molto utile.

+0

Date un'occhiata a questa risposta così: http://stackoverflow.com/a/39692781/205859 –

risposta

4

Sembra che non sia attualmente implementato. Vedi Issue 24: MockSequence. This thread discute il problema.

Si potrebbe considerare di rivedere i test, però. In genere ritengo che l'ordine di test porti a test fragili, poiché spesso verifica i dettagli dell'implementazione.

MODIFICA: Non sono sicuro che questo risolva la domanda dell'OP. La risposta di Lucero potrebbe essere più utile.

+1

L'ordine di verifica/verifica può portare a test fragili ma NON perché sta verificando i dettagli di implementazione. Se stai usando Mocks, specialmente con una classe che usa il pattern Inversion of Control (Dependency Injection), stai già testando i dettagli di implementazione; questo è il punto. Direi che l'ordine di test non dovrebbe essere un modello comune nei test, ma ci sono casi validi in cui non ci sono alternative per guidare il codice corretto con TDD. –

+1

@JesseWebb - Penso che siamo solo in disaccordo sulla semantica.Accettiamo che alcuni test abbiano una conoscenza inappropriata dei dettagli di implementazione interna. Preferisco test che non si interrompano quando il sistema in prova viene refactorato, a patto che l'API pubblica rimanga invariata. Come dici tu, ci sono casi per testare l'ordine, ma non sono comuni. – TrueWill

+0

La differenza tra i test che si stanno descrivendo (riguarda solo l'API del sistema in prova) ei test che utilizzano i mock è che i primi possono essere veri test di unità solo se la classe non ha dipendenze. Se la classe ha delle dipendenze e non si usano i mock, questi sono test funzionali. Sono d'accordo, tuttavia, che ci sia un valore sia nei test black-box che white-box. –

3

Dai un'occhiata a questo blog post, potrebbe risolvere il tuo problema.

+0

Impossibile ottenere questo al lavoro nel mio caso , Non ho potuto usare una coda in cambio dell'array. Non importa però ora. Vedi modifica. – Finglas

+0

Bel collegamento, comunque! – TrueWill

1

In caso contrario, è possibile utilizzare le funzioni di richiamata e incrementare/memorizzare un valore di chiamataIndice.

30

Ho creato di recente Moq.Sequences che consente di controllare l'ordine in Moq. Si consiglia di leggere il mio post che descrive il seguente:

  • Supporta chiamate di metodo, di proprietà setter e getter.
  • Consente di specificare il numero di volte che una chiamata specifica dovrebbe essere prevista.
  • Fornisce loop che consentono di effettuare chiamate di gruppo in un gruppo ricorrente.
  • Consente di specificare il numero di volte che si prevede un ciclo.
  • Le chiamate che dovrebbero essere chiamate in sequenza possono essere interconnesse con le chiamate previste in qualsiasi ordine.
  • Supporto multi-thread.

Uso tipico assomiglia:

[Test] 
public void Should_show_each_post_with_most_recent_first_using_sequences() 
{ 
    var olderPost = new Post { DateTime = new DateTime(2010, 1, 1) }; 
    var newerPost = new Post { DateTime = new DateTime(2010, 1, 2) }; 
    var posts = new List<Post> { newerPost, olderPost }; 

    var mockView = new Mock<BlogView>(); 

    using (Sequence.Create()) 
    { 
     mockView.Setup(v => v.ShowPost(newerPost)).InSequence(); 
     mockView.Setup(v => v.ShowPost(olderPost)).InSequence(); 

     new BlogPresenter(mockView.Object).Show(posts); 
    } 
} 
+2

Questa è un'estensione impressionante di Moq; per me la mancanza di supporto per la sequenza è l'omissione principale del progetto. IMO questo dovrebbe essere tirato nel bagagliaio (https://github.com/dwhelan/Moq-Sequences). – briantyler

+1

cosa dire di eseguire l'invocazione su diversi oggetti moq all'interno di una sequenza - è supportato? – chester89

0

Dal post originale ho potuto supporre che il metodo di prova effettuare le seguenti operazioni chiamare:

var screenOne = new Screen(...); 
var screenTwo = new Screen(...); 
var screens = new []{screenOne, screenTwo}; 
var screenManager = new ScreenManager(screens); 
screenManager.Draw(); 

Dove 'Draw' implementazione del metodo è qualcosa In questo modo:

public class ScreenManager 
{ 
    public void Draw() 
    { 
     _screens[0].Draw(); 
     _screens[1].Draw(); 
    } 
} 

Da m In prospettiva, se l'ordine delle chiamate è molto importante, allora la struttura aggiuntiva (che descrive la sequenza) dovrebbe essere introdotta nel sistema.

Il più semplice implementazione: ogni schermata dovrebbe conoscere il suo elemento successivo e chiamare il suo metodo Draw dopo il disegno stesso:

// 1st version 
public class Screen(Screen screenSubSequent) 
{ 
    private Screen _screenNext; 
    public Screen(Screen screenNext) 
    { 
     _screenNext=screenNext; 
    } 
    public void Draw() 
    { 
     // draw himself 
     if (_screenNext!=null) _screenNext.Draw(); 
    } 
} 

public class ScreenManager 
{ 
    public void Draw() 
    { 
     _screens[0].Draw(); 
    } 
} 

static void Main() 
{ 
    var screenOne = new Screen(null, ...); 
    var screenTwo = new Screen(screenOne, ...); 
    var screens = new []{screenOne, screenTwo}; 
    var screenManager = new ScreenManager(screens); 
} 

Dal un punto, ogni elemento dello schermo dovrebbe conoscere un po 'un altro. Questo non è sempre buono. In tal caso: puoi creare alcune classi come "ScreenDrawer". Questo oggetto memorizza proprio schermo e la successiva schermata (probabilmente lo ereditano dalla classe schermo Utilizzo altri mondi:.. Class 'ScreenDrawer' descrive la struttura del sistema Ecco uno scenario più semplice di realizzazione:

// 2nd version 
public class ScreenDrawer 
{ 
    private Screen _screenNext; 
    public ScreenDrawer(Screen screenNext, ...) : base (...) 
    { 
     _screenNext=screenNext; 
    } 
    public void Draw() 
    { 
     // draw himself 
     if (_screenNext!=null) _screenNext.Draw(); 
    } 
} 

public class ScreenManager 
{ 
    public void Draw() 
    { 
     _screens[0].Draw(); 
    } 
} 

static void Main() 
{ 
    var screenOne = new ScreenDrawer(null, ...); 
    var screenTwo = new ScreenDrawer(screenOne, ...); 
    var screens = new []{screenOne, screenTwo}; 
    var screenManager = new ScreenManager(screens); 
} 

secondo metodo di introdurre eredità supplementare, ma non richiede la classe Screen per conoscere il suo elemento di sottosequenza

Riepilogo: entrambi i metodi eseguono chiamate sequenziali e non richiedono test di "sequenza", ma richiedono il test se lo "schermo" corrente ne chiama un altro e prova se 'ScreenManager' chiama il metodo 'Disegna' del primo elemento in sequenza

Questo approccio:

  1. più verificabili (può essere implementato usando la maggior parte framework di test, senza necessità di sostenere 'sequenza test');
  2. Più stabile (nessuno può modificare facilmente una sequenza: hi avrà bisogno non solo di aggiornare il codice sorgente, ma anche di aggiornare alcuni test);
  3. Più orientato agli oggetti (si sta lavorando con l'oggetto, non con entità astratte come 'sequenza');
  4. Come risultato: molto più comodo.

Grazie.

10

Una soluzione semplice utilizzando Moq richiamate:

[TestMethod] 
    public void CallInOrder() 
    { 
     // Arrange 
     string callOrder = ""; 

     var service = new Mock<MyService>(); 
     service.Setup(p=>p.FirstCall()).Returns(0).CallBack(()=>callOrder += "1"); 
     service.Setup(p=>p.SecondCall()).Returns(0).CallBack(()=>callOrder += "2"); 

     var sut = new Client(service); 

     // Act 
     sut.DoStuff(); 

     // Assert 
     Assert.AreEqual("12", callOrder); 
    } 
+0

Non funziona, si dice che non può dedurre il tipo dall'uso – DaveH

+0

@DaveH hai provato il debug attraverso? Quale linea ha il problema? –

+0

@DaveH invece di "p => callOrder" use "() => callOrder" –

Problemi correlati