2015-09-15 8 views
5

Ich schreibe gerade eine Anwendung und um das richtige Verhalten zu testen, muss ich überprüfen, dass Methoden in einer bestimmten Reihenfolge aufgerufen werden.Versuchen, MockSequence zu verstehen

Für meine Unit-Tests, ich bin xUnit und Moq mit Jetzt

, warum muss ich in der Reihenfolge, Testanrufe gemacht werden?

Ich entwickle eine Lösung, die Aufgaben auf verschiedenen Threads ausführt. Sobald eine Aufgabe ausgeführt wird, schreibe ich eine Nachricht an einen bestimmten Logger. Durch Überprüfung der Reihenfolge, in der die Aufrufe an den Logger vorgenommen werden, bin ich mir sicher, dass mein Code korrekt implementiert wurde.

Sehen Sie hier den Code ein, die ich versuche zu verwenden:

public class SchedulerFixture 
{ 
    #region Constructors 

    public SchedulerFixture() 
    { 
     LoggerMock = new Mock<ILogger>(MockBehavior.Strict); 

     // Setup of other mocks removed for simplicity. 
    } 

    #endregion 
} 

public class SequentialTaskExecutorMock : SchedulerFixture 
{ 
    [Fact] 
    public void Should_WriteTheCorrectLogEntries_WhenTasksAreExecutedAndNotCancelled() 
    { 
     // Defines the task that needs to be executed. 
     var task = new LongRunningServiceTaskImplementation(); 

     // Built a sequence in which logs should be created. 
     var sequence = new MockSequence(); 

     LoggerMock.Setup(x => x.Information(It.IsAny<string>(), It.IsAny<string>())).Verifiable(); 

     LoggerMock.InSequence(sequence).Setup(x => x.Information("émqsdlfk", "smdlfksdmlfk")).Verifiable(); 
     LoggerMock.InSequence(sequence).Setup(x => x.Information(LoggingResources.LoggerTitle, LoggingResources.Logger_ServiceStopped)).Verifiable(); 
     LoggerMock.InSequence(sequence).Setup(x => x.Information(LoggingResources.LoggerTitle, LoggingResources.Logger_ServiceStarted)).Verifiable(); 
     LoggerMock.InSequence(sequence).Setup(x => x.Information(LoggingResources.LoggerTitle, string.Format(CultureInfo.InvariantCulture, LoggingResources.Logger_TaskCompleted, task.TaskName))).Verifiable(); 
     LoggerMock.InSequence(sequence).Setup(x => x.Information(LoggingResources.LoggerTitle, LoggingResources.Logger_ServiceStopped)).Verifiable(); 

     // Setup the mock required for the tests. 
     TaskGathererMock.Setup(x => x.GetServiceTasks(LoggerMock.Object)).Returns(() => 
     { 
      return new[] { task }; 
     }); 

     // Start the scheduler. 
     Scheduler.Start(TaskGathererMock.Object, ConfigurationManagerMock.Object); 

     // Wait for 5 seconds (this simulates running the service for 5 seconds). 
     // Since our tasks execution time takes 4 seconds, all the assigned tasks should have been completed. 
     Thread.Sleep(5000); 

     // Stop the service. (We assume that all the tasks have been completed). 
     Scheduler.Stop(); 

     LoggerMock.VerifyAll(); 
    } 
} 

So, der erste Schritt ist es, die Protokolle zu Setup in meinem Test, dann der Test selbst durchgeführt wird (diese Anrufe an den Logger verursacht) und am Ende verifiziere ich es.

Der Test besteht jedoch immer.

Es sollte seit dem folgenden Aufruf in diesem Fall fehlschlagen:

LoggerMock.InSequence(sequence).Setup(x => x.Information("émqsdlfk", "smdlfksdmlfk")).Verifiable(); 

ist nicht überall in meinem Code ausgeführt.

+4

Siehe [Problem # 75] (https://github.com/Moq/moq4/issues/75), dies kann Verhalten erwartet werden. –

+0

@PatrickQuirk Große Verbindung. Aber wenn man sich die Thread-Kommentare vom 2. Januar 2014 anschaut, ist man sich einig, dass es zwei Probleme gibt (was Moq "Bugs" bedeutet). Was Komplexität in seinem obigen Code erfährt, ist ihr "Problem 1". –

Antwort

1

Während dies sicher wie ein Bug in Moq fühlt (siehe ersten Kommentar zu der Frage, von Patrick Quirk), hier ist eine grobe Idee, was Sie stattdessen tun könnten. Das ist eine Art "langer Kommentar".

Machen Sie eine einfache Klasse wie:

class SequenceTracker 
{ 
    int? state; 

    public void Next(int newState) 
    { 
     if (newState <= state) 
      Assert.Fail("Bad ordering there! States should be increasing."); 

     state = newState; 
    } 
} 

verwenden Sie es dann wie folgt (modifizierte Version des eigenen Code):

public void Should_WriteTheCorrectLogEntries_WhenTasksAreExecutedAndNotCancelled() 
{ 
    // Defines the task that needs to be executed. 
    var task = new LongRunningServiceTaskImplementation(); 

    // USE THE CLASS I PROPOSE: 
    var tracker = new SequenceTracker(); 


    //LoggerMock.Setup(x => x.Information(It.IsAny<string>(), It.IsAny<string>())) 

    LoggerMock.Setup(x => x.Information("émqsdlfk", "smdlfksdmlfk")) 
     .Callback(() => tracker.Next(10)); 
    LoggerMock.Setup(x => x.Information(LoggingResources.LoggerTitle, LoggingResources.Logger_ServiceStopped)) 
     .Callback(() => tracker.Next(20)); 
    LoggerMock.Setup(x => x.Information(LoggingResources.LoggerTitle, LoggingResources.Logger_ServiceStarted)) 
     .Callback(() => tracker.Next(30)); 
    LoggerMock.Setup(x => x.Information(LoggingResources.LoggerTitle, string.Format(CultureInfo.InvariantCulture, LoggingResources.Logger_TaskCompleted, task.TaskName))) 
     .Callback(() => tracker.Next(40)); 
    LoggerMock.Setup(x => x.Information(LoggingResources.LoggerTitle, LoggingResources.Logger_ServiceStopped)) 
     .Callback(() => tracker.Next(50)); 

    // Setup the mock required for the tests. 
    TaskGathererMock.Setup(x => x.GetServiceTasks(LoggerMock.Object)).Returns(() => 
    { 
     return new[] { task }; 
    }); 

    // Start the scheduler. 
    Scheduler.Start(TaskGathererMock.Object, ConfigurationManagerMock.Object); 

    // Wait for 5 seconds (this simulates running the service for 5 seconds). 
    // Since our tasks execution time takes 4 seconds, all the assigned tasks should have been completed. 
    Thread.Sleep(5000); 

    // Stop the service. (We assume that all the tasks have been completed). 
    Scheduler.Stop(); 

    // THIS NOW WORKS BECAUSE WE ABANDONED THE 'MockSequence' APPROACH: 
    LoggerMock.VerifyAll(); 
} 

Natürlich kann diese fortgeschritteneren gemacht werden, wenn gewünscht.