2013-04-04 8 views
8

Sto cercando di verificare quanto segue:simulare un ritardo nella esecuzione in unit test utilizzando Moq

protected IHealthStatus VerifyMessage(ISubscriber destination) 
{ 
    var status = new HeartBeatStatus(); 

    var task = new Task<CheckResult>(() => 
    { 
     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; 
} 

con il seguente test di unità:

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() solo restituisce una stringa di personaggi e il metodo "di ritardo di consegna" è

private List<NcsMessage> 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<NcsMessage>(); 
    messages.Add(msg); 

    Console.WriteLine("DelayDelivery: {0}", DateTime.Now); 
    Thread.Sleep(delay); 
    Console.WriteLine("DelayDelivery: {0}", DateTime.Now); 

    return messages; 
} 

sto usando Moq come il quadro di scherno e MSTest come il testin quadro g. Ogni volta che si esegue il test di unità, ottengo il seguente output:

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 

oltre l'ovvio "odore di codice" utilizzando il Thread.Sleep nei metodi di cui sopra, il risultato del test di unità non è quello che sto cercando realizzare.

Qualcuno può suggerire un modo migliore/preciso di utilizzare il framework Moq per simulare un ritardo nella "consegna" del messaggio. Ho omesso parte del codice "colla" e incluso solo le parti pertinenti. Fammi sapere se qualcosa che ho lasciato fuori che ti impedisce di essere in grado di comprendere la domanda.

+3

Sarebbe meglio creare un esempio che qualcuno (diverso da te) possa effettivamente mettere in evidenza ciò che stai cercando di realizzare. Non c'è niente qui che qualcuno possa copiare e correre senza fare un mucchio di lavoro extra. –

risposta

27

Se si desidera un modello Moq per sedersi e non fare nulla per un po 'è possibile utilizzare un callback:

Mock<IFoo> mockFoo = new Mock<IFoo>(); 
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); 

Ho provato che nel LINQPad e se si regola il Thread.Sleep() il tempo di esecuzione varia di conseguenza .

+0

Il metodo Callback() era quello che mi mancava ... Ho aggiornato il test dell'unità per usarlo e il test dell'unità ora "prende in giro" correttamente il ritardo ... Grazie! –

+4

questo non sembra funzionare per me, penso che il callback sia eseguito dopo che il risultato è stato restituito dal Moq –

+0

Concordo con @DanDinu - se esegui due chiamate simultanee a 'Bar()' allora i due 'Callback' s vengono eseguiti * dopo * entrambi 'Bar()' s completato. – OffHeGoes

0

non ho potuto ottenere la versione Moq al lavoro, così ho finito per fare qualcosa di simile:

un piccolo esempio utilizzando 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(); 

    } 

} 
6

Quando si imposta il finto si può dire il thread sleep in the return func:

Mock<IMyService> myService = new Mock<IMyService>(); 

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