我第一次尝试实施测试驱动开发(TDD)。 我的项目是 dotnet 3.5 中的 c#。 我已经阅读了《Professional Test Driven Development in 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 吗? 感谢您的回复。
您不对服务进行 TDD,而是对您的服务将用于完成其工作的对象进行 TDD。
这有几个优点
底线
因为我 just 在工作中重构了 4 个现有的 Windows 服务,所以我忍不住要补充一个答案!
我所做的是完全剥离 Windows 服务类,并制作我自己的
ServiceBase
类及其 4 个派生类,用于 4 种不同的实现。最根本的原因是,由于其不方便的测试周期,test 你的 windows 服务真的很痛苦:
应用更改、构建、卸载 Windows 服务、安装更新的 Windows 服务、测试、调试和重复...
TDD 对我的 Windows 服务的主要目的是:
我从您的代码示例中认识到相同的需求。请允许我展示我自己的简化代码来描绘您可以做些什么来成功地对您的 Windows 服务进行 TDD。
我将首先展示测试,因为这是有趣的部分。我将在测试下方添加一些已实现类的片段作为参考。
真正的东西开始之前的一些设置......
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 类中使用 your 服务类
(这是视觉基础,但这不会有太大区别)
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
visual basic 类。它创建一个实例
AdministratorAlertingService
.
Private ReadOnly _alertingService As AdministratorAlertingService =
New AdministratorAlertingService(/* parameters /*)
AdministratorAlertingService
扩展了 AdministratorServiceBaseClass
,其中包含我的其他 Windows 服务也具有的共享行为(计时器、启动、暂停、停止)。
如果您只有一个 Windows 服务,那么您当然不需要基类。
在我的单元测试中,我创建了一个新的 SuT(被测对象),在本例中是一个新的
AdministratorAlertingService
,我使用 AutoResetEvent
验证它具有正确的开始、暂停、停止行为。 Windows 服务完成的“实际工作”在专门针对这些类的单元测试中进行了模拟和测试。
这样你就可以(也应该)TDD 你的 windows 服务。它将大大减少您的 Windows 服务的开发测试周期。
您可以选择将集成测试添加到您的测试套件以测试完整的功能:您委托的手写开始、暂停、停止行为,您不模拟执行实际工作的类的功能。我通过 AdministratorServices 获得了最多的 TDD。
希望对你有帮助!享受你的 TDD 冒险。