使用Moq模拟unit testing中的执行延迟

我正在尝试测试以下内容:

protected IHealthStatus VerifyMessage(ISubscriber destination) { var status = new HeartBeatStatus(); var task = new Task(() => { Console.WriteLine("VerifyMessage(Start): {0} - {1}", DateTime.Now, WarningTimeout); Thread.Sleep(WarningTimeout - 500); Console.WriteLine("VerifyMessage(Success): {0}", DateTime.Now); if (CheckMessages(destination)) { return CheckResult.Success; } Console.WriteLine("VerifyMessage(Pre-Warning): {0} - {1}", DateTime.Now, ErrorTimeout); Thread.Sleep(ErrorTimeout - 500); Console.WriteLine("VerifyMessage(Warning): {0}", DateTime.Now); if (CheckMessages(destination)) { return CheckResult.Warning; } return CheckResult.Error; }); task.Start(); task.Wait(); status.Status = task.Result; return status; } 

通过以下unit testing:

 public void HeartBeat_Should_ReturnWarning_When_MockReturnsWarning() { // Arrange var heartbeat = new SocketToSocketHeartbeat(_sourceSubscriber.Object, _destinationSubscriber.Object); heartbeat.SetTaskConfiguration(this.ConfigurationHB1ToHB2_ValidConfiguration()); // Simulate the message being delayed to destination subscriber. _destinationSubscriber.Setup(foo => foo.ReceivedMessages).Returns(DelayDelivery(3000, Message_HB1ToHB2())); // Act var healthStatus = heartbeat.Execute(); // Assert Assert.AreEqual(CheckResult.Warning, healthStatus.Status); } 

Message_HB1ToHB2()只返回一个字符串,“Delay Delivery”方法是

 private List DelayDelivery(int delay, string message) { var sent = DateTime.Now; var msg = new NcsMessage() { SourceSubscriber = "HB1", DestinationSubscriber = "HB2", SentOrReceived = sent, Message = message }; var messages = new List(); messages.Add(msg); Console.WriteLine("DelayDelivery: {0}", DateTime.Now); Thread.Sleep(delay); Console.WriteLine("DelayDelivery: {0}", DateTime.Now); return messages; } 

我使用Moq作为模拟框架,使用MSTest作为测试框架。 每当我运行unit testing时,我得到以下输出:

 DelayDelivery: 04/04/2013 15:50:33 DelayDelivery: 04/04/2013 15:50:36 VerifyMessage(Start): 04/04/2013 15:50:36 - 3000 VerifyMessage(Success): 04/04/2013 15:50:38 

除了在上面的方法中使用Thread.Sleep的明显“代码味道”之外,unit testing的结果不是我想要完成的。

任何人都可以建议一种更好/更准确的方法来使用Moq框架来模拟消息“传递”的延迟。 我遗漏了一些“胶水”代码,只包括相关部分。 让我知道,如果我遗漏了一些东西,阻止你能够理解这个问题。

如果你想让Moq模拟只是坐下来什么也不做,你可以使用回调:

 Mock mockFoo = new Mock(); mockFoo.Setup(f => f.Bar()) .Callback(() => Thread.Sleep(1000)) .Returns("test"); string result = mockFoo.Object.Bar(); // will take 1 second to return Assert.AreEqual("test", result); 

我在LinqPad中尝试过,如果调整Thread.Sleep() ,执行时间会相应变化。

设置模拟时,您可以告诉线程在返回函数中hibernate:

 Mock myService = new Mock(); myService.Setup(x => x.GetResultDelayed()).Returns(() => { Thread.Sleep(100); return "result"; }); 

我无法让Moq版本工作,所以我最终制作了这样的东西:

使用WaitHandle的一个小例子:

 [TestFixture] public class EventWaitHandleTests { class Worker { private volatile bool _shouldStop; public EventWaitHandle WaitHandleExternal; public void DoWork () { while (!_shouldStop) { Console.WriteLine("worker thread: working..."); Thread.Sleep(1000); WaitHandleExternal.Set(); } } public void RequestStop() { _shouldStop = true; } } [Test] public void WaitForHandleEventTest() { EventWaitHandle _waitHandle = new AutoResetEvent (false); // is signaled value change to true // start a thread which will after a small time set an event Worker workerObject = new Worker (); workerObject.WaitHandleExternal = _waitHandle; Thread workerThread = new Thread(workerObject.DoWork); // Start the worker thread. workerThread.Start(); Console.WriteLine ("Waiting..."); _waitHandle.WaitOne(); // Wait for notification Console.WriteLine ("Notified"); // Stop the worker thread. workerObject.RequestStop(); } } 

我喜欢并投票支持serup的解决方案。 我的答案是他转换为用作图书馆的版本。

 using System; using System.Threading.Tasks; using Microsoft.VisualStudio.TestTools.UnitTesting; ///  /// support halting a workflow and waiting for a finish request ///  public class MockWorker { private readonly DateTime? _end; private volatile bool _shouldStop; ///  /// Create a worker object ///  /// How long before DoWork will timeout. default - Null will not timeout. public MockWorker(int? timeoutInMilliseconds = null) { if (timeoutInMilliseconds.HasValue) _end = DateTime.Now.AddMilliseconds(timeoutInMilliseconds.Value); } ///  /// Instruct DoWork to complete ///  public void RequestStop() { _shouldStop = true; } ///  /// Do work async will run until either timeoutInMilliseconds is exceeded or RequestStop is called. ///  public async Task DoWorkAsync() { while (!_shouldStop) { await Task.Delay(100); if (_end.HasValue && _end.Value < DateTime.Now) throw new AssertFailedException("Timeout"); } } ///  /// Do work async will run until either timeoutInMilliseconds is exceeded or RequestStop is called. ///  /// Type of value to return /// The value to be returned /// valueToReturn public async Task DoWorkAsync(T valueToReturn) { await DoWorkAsync(); return valueToReturn; } }