3

私は初めてテスト駆動開発を実装しようとしています。私のプロジェクトはdotnet3.5のac#です。「ProfessionalTestDrivenDevelopment 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を実装する方法を教えてください。返信ありがとうございます。

4

2 に答える 2

6

サービスをTDDするのではなく、サービスがその仕事をするために使用するオブジェクトをTDDします。

これにはいくつかの利点があります

  • オブジェクトは、サービスやGUIなどで使用されるかどうかに関係なく、最初からありません。
  • サービスを開始、テスト、停止するよりも、単体テストフレームワークでプレーンオブジェクトを作成、テスト、破棄する方がはるかに簡単で高速です。

結論

  • 独自のコードをTDDし、サードパーティのセットアップを方程式から除外します(他の人々のコードをTDDすることは、そもそも撞着語です:))
  • サービスにオブジェクトを使用させます。
  • テストケースを自動化します。
于 2013-02-18T10:13:39.943 に答える
4

仕事で4つの既存のWindowsサービスをリファクタリングしたばかりなので、追加の回答を投げることに抵抗できません!

私が行ったことは、Windowsサービスクラスを完全に削除しServiceBase、4つの異なる実装用の4つの派生物を使用して独自のクラスを作成することです。最も基本的な理由は、テストサイクルが不便なため、Windowsサービスをテストするのが非常に難しいことです。

変更の適用、ビルド、Windowsサービスの削除、更新されたWindowsサービスのインストール、テスト、デバッグとの闘い、繰り返し...

私にとってWindowsサービスをTDDする主な目的は次のとおりです。

  • すべてのデッドロックの問題に取り組みます。
  • 他のオブジェクトへの委任された呼び出しが呼び出されることを確認します。
  • 開発とテストのサイクルを大幅に短縮して、開発をスピードアップしましょう!

私はあなたのコード例から同じニーズを認識しています。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クラスでサービスクラスを使用する方法

(これはビジュアルベーシックですが、それほど大きな違いはありません)

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

2日間で4つのWindowsサービスをリファクタリングしましたが、そのメリットは計り知れません。TDDは、私が品質を提供するのに本当に役立ちました。


コメントへの返信

私のWindowsサービスクラスはService1ビジュアルベーシッククラスです。のインスタンスを作成します AdministratorAlertingService

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

は、他のWindowsサービスにもある共有動作(タイマー、開始、一時停止、停止)を含むをAdministratorAlertingService拡張します。AdministratorServiceBaseClass

Windowsサービスが1つしかない場合は、もちろん基本クラスは必要ありません。

私の単体テストでは、新しいSuT(テスト対象)を作成します。この場合は新しいAdministratorAlertingServiceものであり、を使用して、開始、一時停止、停止の動作が正しいことを確認しますAutoResetEvent。Windowsサービスによって実行される「実際の作業」は、これらのクラス専用の単体テストでモックされ、テストされます。

そうすれば、WindowsサービスをTDDすることができます(そしてそうすべきです)。これにより、Windowsサービスの開発テストサイクルが大幅に短縮されます。

テストスイートに統合テストを追加して、完全な機能をテストすることを選択できます。委任された手書きの開始、一時停止、停止の動作で、実際の作業を行うクラスの機能を模倣しません。私はAdministratorServicesで最も多くのTDDを獲得しました。


お役に立てば幸いです。TDDの冒険をお楽しみください。

于 2013-02-18T18:16:00.293 に答える