我正在编写一个应用程序并测试它的正确行为,我需要验证方法是否按给定顺序调用。
现在,为什么我需要测试调用的顺序?
我正在开发一个在不同线程上执行任务的解决方案。 一旦执行任务,我就会向给定的记录器写一条消息,因此通过检查对记录器的调用顺序,我确信我的代码已正确实现。
在这里看到我正在尝试使用的代码:
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();
}
}
因此,我测试的第一步是设置日志,然后执行测试本身(这会导致调用记录器),最后我正在验证它。
然而,测试确实经过。
在这种情况下它应该失败,因为以下调用:
LoggerMock.InSequence(sequence).Setup(x => x.Information("émqsdlfk", "smdlfksdmlfk")).Verifiable();
我的代码中没有执行任何内容。
答案 0 :(得分:1)
虽然这确实感觉像是Moq中的一个错误(请参阅Patrick Quirk提出的问题的第一个评论),但这是一个粗略的想法,你可以做什么。这是一个长期评论&#34;。
创建一个简单的类:
class SequenceTracker
{
int? state;
public void Next(int newState)
{
if (newState <= state)
Assert.Fail("Bad ordering there! States should be increasing.");
state = newState;
}
}
然后像这样使用它(你自己的代码的修改版本):
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();
}
当然,如果需要,这可以更加先进。
答案 1 :(得分:0)
受到杰普·斯蒂格·尼尔森(Jeppe Stig Nielsen)的回答的启发,我继续了他的想法,我认为这在某种程度上解决了您的问题(我希望能在Moq中完成)。 这将验证模拟序列的完整执行:
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++;
}
}
用法:
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();
}