C # TDD first time in ServiceBase

I am trying to implement test development for the first time. My project is C # at dotnet 3.5. I have read the book Professional Test Driven Development in C # and now I want to test my project that contains a windows service. I read that the best practice is that all code should be tested. Below is my windows service which implements onStart and onStop method

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();
        }

      }
    }
   }

      

Can you tell me how can I implement tdd here? Thanks for the answer.

+3


source to share


2 answers


Since I was just refactoring 4 existing Windows services at work, I couldn't resist answering a supplementary answer!

What I have done is to completely separate the Windows service class and create its own class ServiceBase

with its 4 derivatives for 4 different implementations. The most fundamental reason is that it is a real pain to test your Windows service due to the awkward test cycle:

Apply change, Build, Deinstall windows service, Install updated windows service, Check, Fight debugging and Retry ...

The main purpose of TDD for windows service for me is:

  • Solve all deadlock problems.
  • Check that calls to other objects are called.
  • It's hard to shorten the development cycle to speed up development!

I am learning the same needs from your example code. Let me show you my own simplified code to draw a picture of what you could do to successfully TDD your Windows service (s).

I'll show the tests first, as this is the interesting part. I'll add some snippets of the implemented classes as a link below the tests.

My [SetUp] and unit tests

Some settings before the start of the real stuff ...

    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);
    }

      

Check the behavior 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();
    }

      

Check the behavior 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();
    }

      

My implementation of service functionality

An excerpt from the interesting and most significant parts:

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() {}
    // ..
    // ..
}

      



How do you use your service class in a real WindowsService class

(this is a visual baseline, but it won't matter much)

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

      


I re-processed 4 windows of service in two days and the benefits are immeasurable! TDD has really helped me deliver quality.


Reply to your comment

My Windows Service class is a Service1

visual base class. It creates an instance AdministratorAlertingService

.

Private ReadOnly _alertingService As AdministratorAlertingService = 
    New AdministratorAlertingService(/* parameters /*)

      

AdministratorAlertingService

extends AdministratorServiceBaseClass

that contains the concurrent behavior that my other windows services have (timer, start, pause, stop).

If you only have one Windows service, you don't need a base class, of course.

In my unit tests, I create a new SuT (subject under test), which in this case is new AdministratorAlertingService

, and I verify that it has the correct start, pause, stop using AutoResetEvent

. The "actual work" done by the Windows service is mocked and unit tested specifically for these classes.

This way you can (and should) TDD use your Windows services. This will greatly shorten your development cycles for your window.

You can add integration tests to your test suite to test the full functionality: manually delegated start, pause, stop behavior when you are not mocking the functionality of the classes that do the actual work. I've got most of the TDDing of my admin services running.


Hope this helps! Enjoy the TDD adventure.

+4


source


You are not a TDD service , but objects that your service will use to do its job.

This has several advantages

  • Your objects will be run agnostic if they are used by a service, GUI or whatever.
  • It is much easier and faster to create, test, and destroy simple objects in any unit test framework than it is to start, test, and stop a service.


Bottom line

  • TDD your own code and leave as many third-party settings out of the equation as possible (TDD'ing other peoples code is an oxymoron in the first place :))
  • Let the service use your objects.
  • Automate your test files.
+6


source







All Articles