Trying To Understand MockSequence

I am currently writing an application and checking its correct behavior. I need to check if methods are called in the given order.

For my unit tests I use xUnit and Moq

Now, why should I check the order in which calls are made?

I am developing a solution that performs tasks for different threads. As soon as the task is completed, I write a message to the specified registrar, therefore, checking the order in which the registrar calls, I am sure that my code was correctly implemented.

See here the code I'm trying to use:

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, the first step in my testing is to configure the logs, then the test itself is performed (this causes calls to the registrar), and in the end I check it.

However, the test always passes.

In this case, it should fail because the following call:

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

Not executed anywhere in my code.

+5
source share
2 answers

While this surely seems like a bug in Moq (see the first comment on the question by Patrick Quirk), here is a rough idea of ​​what you could do instead. This is a kind of "long comment."

Make a simple class:

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

Then use it like this (modified version of your own 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(); } 

Of course, if desired, this can be made more advanced.

+1
source

Inspired by Jepp Stig Nielsen's answer, I continued with his idea, which I think solves your problem somewhat (and which I would prefer to complete in Moq). This will verify the complete execution of your simulated sequence:

 public class SequenceVerifyer { public MockSequence Sequence { get; private set; } = new MockSequence(); public Action NextCallback() { var callNo = setupCount++; return () => { AssertCallNo(callNo);}; } public void VerifyAll() { Assert.AreEqual(setupCount, executionCount, $"less calls ({executionCount}) executed than previously set up ({setupCount})."); } private int executionCount = 0; private int setupCount = 0; private void AssertCallNo(int expectedCallNo) { Assert.AreEqual(executionCount, expectedCallNo, $"order of call is wrong. this call is marked with No ({expectedCallNo}), but ({executionCount}) was expected."); executionCount++; } } 

using:

 public interface IFoo { bool foo(int n); } public interface IBar { int bar(string a); } [Test] public void SequenceVerifyer_with_full_sequence() { var fooMock = new Mock<IFoo>(MockBehavior.Strict); var barMock = new Mock<IBar>(MockBehavior.Strict); var seq = new SequenceVerifyer(); fooMock.Setup(f => f.foo(3)).Returns(false); barMock.Setup(b => b.bar("world")).Returns(4); fooMock.InSequence(seq.Sequence).Setup(f => f.foo(4)).Returns(true).Callback(seq.NextCallback()); barMock.InSequence(seq.Sequence).Setup(b => b.bar("hello")).Returns(2).Callback(seq.NextCallback()); fooMock.InSequence(seq.Sequence).Setup(f => f.foo(4)).Returns(false).Callback(seq.NextCallback()); fooMock.Object.foo(3); //non sequence fooMock.Object.foo(4); barMock.Object.bar("hello"); barMock.Object.bar("world"); //non sequence fooMock.Object.foo(4); fooMock.VerifyAll(); barMock.VerifyAll(); seq.VerifyAll(); } 
0
source

All Articles