2009-11-19 18 views
27

Im Moment zu testen ich habe:Wie Methodenaufruf, um mit 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"); 
    } 

Aber die Reihenfolge, in der ich meine Objekte in dem Produktionscode ziehe keine Rolle spielt. Ich könnte einen zuerst machen, oder zwei, es spielt keine Rolle. Es sollte jedoch wichtig sein, wie die Zeichnungsreihenfolge wichtig ist.

Wie stellen Sie (mit Moq) sicher, dass Methoden in einer bestimmten Reihenfolge aufgerufen werden?

bearbeiten

habe ich diesen Test befreien. Die Zeichenmethode wurde aus meinen Komponententests entfernt. Ich muss es nur manuell testen, es funktioniert. Die Umkehrung der Bestellung wurde jedoch in eine separate Testklasse genommen, wo sie getestet wurde, so dass es nicht alles schlecht ist.

Danke für den Link über die Funktion, die sie untersuchen. Ich hoffe, dass es bald hinzugefügt wird, sehr praktisch.

+0

Schauen Sie auch bei dieser Antwort: http://stackoverflow.com/a/39692781/205859 –

Antwort

4

Es scheint, dass es derzeit nicht implementiert ist. Siehe Issue 24: MockSequence. This thread bespricht das Problem.

Sie könnten jedoch in Betracht ziehen, Ihre Tests zu überarbeiten. Ich bin generell der Ansicht, dass die Testreihenfolge zu fragilen Tests führt, da oft Implementierungsdetails getestet werden.

EDIT: Ich bin mir nicht sicher, dass dies die Frage des OP adressiert. Luceros Antwort könnte hilfreicher sein.

+1

Das Bestätigen/Verifizieren der Reihenfolge kann zu fragilen Tests führen, aber NICHT, weil die Implementierungsdetails getestet werden. Wenn Sie Mocks verwenden, insbesondere mit einer Klasse, die das Muster Inversion of Control (Dependency Injection) verwendet, testen Sie bereits Implementierungsdetails. Das ist der Punkt. Ich würde sagen, dass die Testreihenfolge in Ihren Tests kein übliches Muster sein sollte, aber es gibt gültige Fälle, in denen es keine Alternativen gibt, um den richtigen Code mit TDD zu fahren. –

+1

@JesseWebb - Ich denke, wir sind nur über die Semantik uneinig.Lassen Sie uns zustimmen, dass einige Tests unangemessene Kenntnisse der internen Implementierungsdetails aufweisen. Ich bevorzuge Tests, die nicht unterbrochen werden, wenn das zu testende System refaktoriert wird, solange die öffentliche API unverändert bleibt. Wie Sie sagen, gibt es Fälle für das Testen der Reihenfolge, aber sie sind nicht üblich. – TrueWill

+0

Der Unterschied zwischen den Tests, die Sie beschreiben (betreffen nur die API des zu testenden Systems) und Tests, die Mocks verwenden, besteht darin, dass erstere nur echte Komponententests sein können, wenn die Klasse keine Abhängigkeiten aufweist. Wenn die Klasse Abhängigkeiten aufweist und Sie keine Mocks verwenden, handelt es sich um Funktionstests. Ich stimme jedoch zu, dass sowohl bei Black-Box- als auch bei White-Box-Tests ein Mehrwert besteht. –

3

Werfen Sie einen Blick auf diese blog post, kann es Ihr Problem lösen.

+0

bekommen konnte dies nicht in meinem Fall zu arbeiten Ich konnte keine Warteschlange als Ersatz für das Array verwenden. Ist jetzt aber egal. Siehe Bearbeiten. – Finglas

+0

Netter Link, obwohl! – TrueWill

1

Andernfalls hätten Sie die Callback-Funktionen verwenden und einen CallIndex-Wert erhöhen/speichern können.

30

Ich habe vor kurzem Moq.Sequences erstellt, die die Möglichkeit bietet, die Reihenfolge in Moq zu überprüfen. Vielleicht möchten Sie meine post, die beschreibt folgendes lesen:

  • Unterstützt Methode Anrufungen, Eigentum Getter und Setter.
  • Ermöglicht Ihnen die Angabe der Anzahl von mal einen bestimmten Anruf sollte erwartet werden.
  • Bietet Schleifen, die es Ihnen ermöglichen, Gruppenrufe in eine wiederkehrende Gruppe .
  • Ermöglicht Ihnen die Angabe der Anzahl von Zeiten, die eine Schleife erwartet werden soll.
  • Anrufe, die voraussichtlich in Reihenfolge genannt werden, können mit Aufrufen gemischt werden, die in beliebiger Reihenfolge erwartet werden.
  • Multi-Thread-Unterstützung.

Typische Anwendungen wie folgt aussehen:

[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

Dies ist eine wunderbare Erweiterung zu Moq; für mich ist fehlende Sequenzunterstützung die Hauptunterlassung im Projekt. IMO sollte dies in den Kofferraum gezogen werden (https://github.com/dwelan/Moq-Sequences). – briantyler

+1

Was ist mit dem Aufruf von verschiedenen Moq-Objekten in einer Sequenz - wird das unterstützt? – chester89

0

Von der ursprünglichen Nachricht ich, dass das Testverfahren übernehmen könnte nennen die folgenden Operationen:

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

Wo ‚Draw‘ Methode Umsetzung ist etwas, wie folgt:

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

Von m Wenn die Reihenfolge der Aufrufe sehr wichtig ist, sollte eine zusätzliche Struktur (die die Sequenz beschreibt) in das System eingeführt werden.

Die einfachste Implementierung: jeder Bildschirm sein nachfolgendes Element kennen sollte und seine Draw-Methode nennt sich nach dem Ziehen:

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

Von dem einem Punkt, jedes Element Bildschirm ein wenig über eine anderen wissen sollte. Das ist nicht immer gut. Wenn ja, können Sie eine Klasse wie 'ScreenDrawer' erstellen. Diese Aufgabe wird eigenen Bildschirm speichern und nachfolgenden Bildschirm (wahrscheinlich ihn von Screen-Klasse erbt andere Welten verwenden:.. ‚ScreenDrawer‘ Klasse Systemstruktur beschreibt hier ein einfachste Szenario der Implementierung ist:

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

2. Methode einführte zusätzliche Vererbung, aber nicht benötigt Screen-Klasse über sein Subsequence-Element zu wissen

Zusammenfassung: beide Methoden tun subsequentielle Aufrufe und erfordert keine "Sequenz" -Test. Stattdessen erfordern sie zu testen, wenn aktuelle "Bildschirm" ein anderes und ruft Testen, ob 'ScreenManager' die 'Draw'-Methode des ersten Elements der Reihe nach aufruft

Dieser Ansatz:

  1. Mehr prüfbar (ohne Notwendigkeit mit den meisten Test-Framework implementiert werden ‚-Sequenz Prüfung‘ zu unterstützen);
  2. Stabiler (niemand kann eine Sequenz leicht ändern: hi muss nicht nur den Quellcode aktualisieren, sondern auch einige Tests aktualisieren);
  3. Mehr objektorientiert (Sie arbeiten mit Objekt, nicht mit abstrakten Entitäten wie 'Sequenz');
  4. Als Ergebnis: viel mehr unterstützbar.

Danke.

10

Eine einfache Lösung mit Moq Rückrufe:

[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

Funktioniert nicht, es sagt, es kann nicht den Typ von der Verwendung – DaveH

+0

@DaveH haben Sie versucht Debugging durch? Welche Linie hat das Problem? –

+0

@DaveH anstelle von "p => CallOrder" verwenden "() => CallOrder" –