如何对心跳模式进行单元测试?

时间:2011-02-28 17:24:56

标签: c# .net unit-testing nunit

我正在编写一个客户端,每秒向服务器发送一个“心跳信号”。客户端在后台线程中调用WCF服务以报告其活动。

如何对此进行单元测试?我是否需要等待几秒钟并检查是否多次调用了适当的方法?

是否应该进行某种情景测试?也许我不应该担心在整个客户的整个生命周期中不断调用服务?

我可以测试对WCF服务的单个调用,但它不会测试“心跳模式”。

我正在使用TDD方法。 (C#,NUnit,Moq)

有任何建议或示例吗?

修改

我认为还不够清楚。

这是我所拥有的更简单的版本:

public class FeedService
{
   private Timer t;

   public FeedService()
   {
      t.Interval = 1000;
      t.Elapsed += TimerElapsed;
      t.Start();
   }

   private void TimerElapsed(object sender, ElapsedEventArgs e)
   {
      t.Stop();
      SendHeartbeat();
      t.Start();
   }
}

......这是我的考验:

[Test]
public void Heartbeat_called_twice_after_2_seconds()
{
  var mockFeedService = new Mock<FeedService>();

  Thread.Sleep(2000);
  mockFeedService.Verify(x => x.SendHeartBeat(), Times.AtLeast(2));
}

我有两个问题:
1)为什么我的测试总是失败?我做错了什么? 2)我应该测试一下吗?

3 个答案:

答案 0 :(得分:5)

您必须首先隔离要测试的功能。例如,在这种情况下,可以测试两个方面。一个是listenbeat组件实际上在指定的时间表上发送心跳消息。另一个是服务接收消息。如果您抽象服务,那么您可以独立于服务实现测试听力组件。这可以在单元测试中通过启动听力组件然后休眠,然后验证存根或模拟服务实现接收到预期数量的消息来完成。确保服务正在接收消息的测试是integration test,因此不是“纯”单元测试。但是,由于必须进行测试,因此您也可以使用测试用例。

答案 1 :(得分:2)

我会在“网关”类型类中包含与服务的交互。这可以在测试中由模拟替换,模拟可以计算您所做的调用,或者您可能需要的任何统计信息。

此致 的Morten

答案 2 :(得分:0)

您要测试的方案,严格来说,是一种集成测试。

我采用的类似测试构建方案的方法是使用这个方便的功能:

/// <summary>
/// Wait no longer than @waitNoLongerThanMillis for @thatWhatWeAreWaitingFor to return true.
/// Tests every second for the 
/// </summary>
/// <param name="thatWhatWeAreWaitingFor">Function that when evaluated returns true if the state we are waiting for has been reached.</param>
/// <param name="waitNoLongerThanMillis">Max time to wait in milliseconds</param>
/// <param name="checkEveryMillis">How often to check for @thatWhatWeAreWaitingFor</param>
/// <returns></returns>
private bool WaitFor(Func<bool> thatWhatWeAreWaitingFor, int checkEveryMillis, int waitNoLongerThanMillis)
{
    var waitedFor = 0;
    while (waitedFor < waitNoLongerThanMillis)
    {
        if (thatWhatWeAreWaitingFor()) return true;

        Console.WriteLine("Waiting another {0}ms for a situation to occur.  Giving up in {1}ms ...", checkEveryMillis, (waitNoLongerThanMillis - waitedFor));
        Thread.Sleep(checkEveryMillis);
        waitedFor += checkEveryMillis;
    }
    return false;
}

用法:

// WaitFor (transaction to become failed, checkEverySoOften, waitNoLongerThan)
int wait = (Settings.EventHandlerCoordinatorNoActivitySleepTime + 5) * 1000;
var failedEventExists = WaitFor(() => EventQueueManager.GetFailedEvents(0, 10).TotalRecords > 0, checkEveryMillis: 1000, waitNoLongerThanMillis: wait);

if (!failedEventExists)
    Assert.Fail("Waited longer than " + wait + " without any evidence of the event having been handled.  Expected to failed event on the queue.");

它使用自.Net4以来仅提供的c#功能,但这些日子可能适合大多数.Net开发人员。