我正在尝试首次实现测试驱动开发。 我的项目是dotnet 3.5中的c#。 我已经阅读了c#中的专业测试驱动开发书,现在我想测试包含windows服务的项目。我已经读过最佳实践是所有代码都必须在测试中。以下是我的Windows服务实现方法onStart和onStop
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.ServiceProcess;
using System.Text;
using System.Threading;
using log4net;
namespace MyUcmaService
{
public partial class MyUcmaService : ServiceBase
{
private Worker _workerObject;
private static MyUcmaService aMyUcmaService;
private Thread _workerThread;
private static ILog _log = LogManager.GetLogger(typeof(MyUcmaService));
public MyUcmaService()
{
InitializeComponent();
aMyUcmaService = this;
}
protected override void OnStart(string[] args)
{
// TODO: inserire qui il codice necessario per avviare il servizio.
//Debugger.Launch();
AppDomain.CurrentDomain.UnhandledException += AppDomainUnhandledException;
try
{
_workerObject = new Worker();
_workerThread = new Thread(_workerObject.DoWork);
// Start the worker thread.
_workerThread.Start();
}
catch (Exception ex)
{
HandleException(ex);
}
}
protected override void OnStop()
{
// TODO: inserire qui il codice delle procedure di chiusura necessarie per arrestare il servizio.
try
{
_workerObject.RequestStop();
_workerThread.Join();
}
catch (Exception ex)
{
HandleException(ex);
}
}
private static void AppDomainUnhandledException(object sender, UnhandledExceptionEventArgs e)
{
HandleException(e.ExceptionObject as Exception);
}
private static void HandleException(Exception ex)
{
if (ex == null)
return;
_log.Error(ex);
if (aMyUcmaService != null)
{
aMyUcmaService.OnStop();
}
}
}
}
你能告诉我怎样才能在这里实施tdd? 谢谢你的回复。
答案 0 :(得分:6)
您没有TDD服务,而是您的服务将用于完成其工作的对象。
这有几个优点
底线
答案 1 :(得分:4)
由于我只是在工作中重构了4个现有的Windows服务,我无法忍受额外的答案!
我所做的是完全剥离Windows服务类,并为4种不同的实现创建我自己的ServiceBase
类及其4个衍生物。最根本的原因是,由于测试周期不方便,测试你的Windows服务真的很痛苦:
应用更改,构建,卸载Windows服务,安装更新的Windows服务,测试,通过调试进行斗争,然后重复...
为我提供TDD服务的主要目的是:
我从您的代码示例中认识到了相同的需求。请允许我展示我自己的简化代码,以便绘制一张图片,说明如何成功TDD您的Windows服务。
我将首先展示测试,因为这是有趣的部分。我将在测试下面添加一些已实现类的片段作为参考。
真正的东西开始之前的一些设置......
private MockRepository _mocks;
private IAdminLayer _adminLayer;
private IAlertSchedule _alertingServices;
private IAlertManager _alertingManager;
private AutoResetEvent _isExecutedSuccesful;
private AdministratorAlertingService _alertingService;
[SetUp]
public void Setup()
{
_isExecutedSuccesful = new AutoResetEvent(false);
_mocks = new MockRepository();
_adminLayer = _mocks.DynamicMock<IAdminLayer>();
_alertingServices = _mocks.DynamicMock<IAlertSchedule>();
_alertingManager = _mocks.DynamicMock<IAlertManager>();
var settings = _mocks.DynamicMock<ISettingsService>();
using (_mocks.Record())
{
Expect.Call(_adminLayer.LogSource).Return("myLogSource").Repeat.Any();
Expect.Call(_adminLayer.Settings).Return(settings);
Expect.Call(settings.IsInitialised()).Return(true);
Expect.Call(settings.GetAlertSchedule()).Return(_alertingServices);
}
_alertingService = new AdministratorAlertingService(_adminLayer, null);
}
测试OnStart
行为:
[Test]
public void AlertingServiceTestOnStart()
{
new Thread(ExecuteOnStart).Start();
Assert.IsTrue(_isExecutedSuccesful.WaitOne());
Assert.IsTrue(_alertingService.ServiceTimer.Enabled);
}
private void ExecuteOnStart()
{
_alertingService.OnStart();
_isExecutedSuccesful.Set();
}
测试OnPause
行为:
[Test]
public void AlertingServiceTestOnPause()
{
new Thread(ExecuteOnPause).Start();
Assert.IsTrue(_isExecutedSuccesful.WaitOne());
Assert.IsFalse(_alertingService.ServiceTimer.Enabled);
}
private void ExecuteOnPause()
{
_alertingService.OnPause();
_isExecutedSuccesful.Set();
}
有趣且最有意义的部分片段:
public abstract class AdministratorServiceBase
{
protected readonly IAdminLayer AdminLayer;
protected readonly ServiceBase Service;
public Timer ServiceTimer = new Timer();
protected AutoResetEvent ResetEvent = new AutoResetEvent(true);
protected AdministratorServiceBase(IAdminLayer adminLayer, ServiceBase service, string name, string logname, string logsource, string version)
{
// Removed irrelevant implementation
ServiceTimer.Elapsed += ServiceTimerElapsed;
}
public virtual void OnStart()
{
try { // Removed irrelevant implementation }
catch (Exception ex)
{
HandleException(" detected a failure (trying to start).", ex, true, true);
}
}
// Same story for the other service methods...
public virtual void OnPause() {}
public virtual void OnContinue() {}
// ..
// ..
}
如何在真实的WindowsService类中使用您的服务类
(这是视觉基础,但这不会产生太大影响)
Public Class Service1
Private ReadOnly _alertingService As AdministratorAlertingService = New AdministratorAlertingService(AdminLayer.GetSingleInstance(), Me)
Protected Overrides Sub OnStart(ByVal args() As String)
_alertingService.OnStart()
End Sub
Protected Overrides Sub OnPause()
_alertingService.OnPause()
End Sub
// etc etc
End Class
我在两天内重构了4个Windows服务,其好处是无法测量的! TDD真的帮助我提供了质量。
我的Windows服务类是Service1
可视基本类。它创建了一个实例
AdministratorAlertingService
。
Private ReadOnly _alertingService As AdministratorAlertingService =
New AdministratorAlertingService(/* parameters /*)
AdministratorAlertingService
扩展了AdministratorServiceBaseClass
,其中包含我的其他Windows服务所具有的共享行为(计时器,开始,暂停,停止)。
如果您只有一个Windows服务,那么您当然不需要基类。
在我的单元测试中,我创建了一个新的SuT(受测试的主题),在这种情况下是一个新的AdministratorAlertingService
,我使用AutoResetEvent
验证它是否具有正确的开始,暂停,停止行为。 Windows服务完成的“实际工作”在专门针对这些类的单元测试中进行模拟和测试。
这样你就可以(并且应该)TDD你的Windows服务。它将大大减少您的Windows服务的开发测试周期。
您可以选择将集成测试添加到测试套件中以测试完整的功能:您委派的手写开始,暂停,停止行为,您不会模拟执行实际工作的类的功能。我获得了最多TDDing我的管理员服务。
我希望它有所帮助!享受您的TDD冒险。