C# 对Windows服务事件处理程序进行单元测试

C# 对Windows服务事件处理程序进行单元测试,c#,.net,multithreading,unit-testing,tdd,C#,.net,Multithreading,Unit Testing,Tdd,我正在编写一个Windows服务,我想针对事件处理程序编写单元测试,以检查它们如何管理工作线程。我已经很顺利地完成了启动、停止和暂停,但是继续进行会让我有些焦虑 Windows服务 Program.Main() 这并没有什么不寻常的事情——它只是设置要运行的服务 MyService.cs 这有一个手动重置事件: private static ManualResetEvent _resetEvent = new ManualResetEvent(false); MyService.OnStart

我正在编写一个Windows服务,我想针对事件处理程序编写单元测试,以检查它们如何管理工作线程。我已经很顺利地完成了启动、停止和暂停,但是继续进行会让我有些焦虑

Windows服务

Program.Main()

这并没有什么不寻常的事情——它只是设置要运行的服务

MyService.cs 这有一个手动重置事件:

private static ManualResetEvent _resetEvent = new ManualResetEvent(false);
MyService.OnStart()

这将创建一个工作线程,以便主线程可以继续侦听来自服务控制管理器的事件

MyService.OnStop()

这将检查Workerthread.IsAlive是否为true,如果为true,则对其调用.Abort()

MyService.OnPause()

这将检查工作线程的线程状态是否为Suspended | SuspendRequested,如果不是,则在| resetEvent上调用.Reset()

注意:目前不确定这些是否是理想状态,或者我是否应该查看状态“WaitSleepJoin”

MyService.OnContinue()

这将检查工作线程的线程状态是否为Suspended | SuspendRequested,如果是,则在_resetEvent上调用.Set()

MyTests

OnStart()测试

这很简单,我只调用事件处理程序,它发现没有工作线程,所以创建了一个

OnStop()测试

稍微复杂一点。在测试中,我创建了一个工作线程,该线程调用一个委托,该委托将其发送到睡眠状态。然后,我使用反射将其设置为服务中的工作线程私有属性,然后调用OnStop()事件处理程序。这将查找正在睡眠的已启动工作线程,因此将中止它

OnPause()测试

与OnStop()测试类似,但我没有创建睡眠线程,而是创建了一个循环,循环的周期很小(足够长,可以持续几秒钟)。当我调用OnPause()方法时,它会找到正在工作的工作线程,并在_resetEvent上调用.Reset()

OnContinue()测试

因此,我的计划是对OnPause和OnStop测试进行模式化,但这次创建了一个当前挂起的工作线程。但是,在.NET4.5中,Thread.Suspend()已过时

因此,我创建了一个本地ManualResetEvent[每个单元测试一个;我对OnContinue()事件处理程序进行了多个单元测试],并在工作线程正在运行的委托方法中,将其设置为call.WaitOne()。如果我运行一个测试,那么它工作正常。但是如果我同时运行它们,那么每个测试的唯一ManualReset事件似乎会以某种方式影响其他测试

仅一个单元测试中的示例代码,我在其中创建工作线程并将其注入服务的私有线程方法:

const BindingFlags InstanceFlags = 
    BindingFlags.Instance | BindingFlags.NonPublic;

PropertyInfo prop = t.GetProperty("MyWorkerThread", InstanceFlags);
EventWaitHandle wh2 = new ManualResetEvent(true);
var thread = new Thread(() =>
  {
    var sb = new Lazy<StringBuilder>();
    for (int i = 0; i < 10000000; i++)
    {
      wh2.WaitOne();
      sb.Value.Append(i.ToString());
    }
  });

thread.Start();
prop.SetValue(service, thread, null);
const BindingFlags InstanceFlags=
BindingFlags.Instance | BindingFlags.NonPublic;
PropertyInfo prop=t.GetProperty(“MyWorkerThread”,InstanceFlags);
EventWaitHandle wh2=新手动重置事件(真);
变量线程=新线程(()=>
{
var sb=新的Lazy();
对于(int i=0;i<10000000;i++)
{
wh2.WaitOne();
sb.Value.Append(i.ToString());
}
});
thread.Start();
属性设置值(服务、线程、空);
因此,当我在另一个测试中重复这一点,但使用“wh3”而不是“wh2”,那么我的测试似乎都会被自己绊倒

工具 使用Visual Studio 2013和NUnit测试运行程序

如果有人能指出我错在哪里,或者更好的是,给我指出一个更好的方法来实现这一点,我将不胜感激

非常感谢


Griff

我重新处理了逻辑,不再担心线程可能处于何种状态。无论发生什么,它都应该调用ManualResetEvent上的相关方法。