簡體   English   中英

單元測試使用Timer的類

[英]Unit testing a class that uses a Timer

我有一個具有私有成員的類,該私有成員具有類型System.Windows.Forms.Timer 每當我的計時器計時時,還會調用一個私有方法。

  1. 是否值得測試該方法? (因為它是私人的)
  2. 我該如何測試? (我知道我可以讓我的測試類繼承要測試的類...)
  3. 我應該嘲笑我的計時器嗎? 因為如果我必須測試使用內部計時器的類,那么我的測試可能會花費很多時間才能完成,對嗎?

編輯:

實際上,該方法依賴於計時,這是代碼:

private void alertTick(object sender, EventArgs e) {
    if (getRemainingTime().Seconds <= 0) {
        Display.execute(Name, WarningState.Ending, null);
        AlertTimer.Stop();
    }
    else {
        var warning = _warnings.First(x => x == getRemainingTime());

        if (warning.TotalSeconds > 0)
            Display.execute(Name, WarningState.Running, warning);
    }
}

如您所見,如果計時器正在運行,它將使用與結束時(剩余時間等於0)不同的參數調用Display.execute() )。 那會是設計上的問題嗎?

  1. 您不是在測試方法(私有方法還是公共方法),而是在驗證類的行為。 而且,如果您尚未驗證某些行為,那么您就無法確定它已實現。 有多種方法可以調用此行為-類的公共接口,或某些依賴事件。 行為調用也不會更改公共接口所達到的某些功能,與依賴項的交互也很重要。
  2. 請參見下面的示例-它顯示了如何測試這種“隱藏”行為。
  3. 請參見下面的示例-它顯示了如何拆分職責,注入依賴關系並模擬它們。

實際上,您的班級承擔了太多的責任-一個是安排一些任務,另一個是執行一些動作。 嘗試將您的班級分為兩個單獨的班級,並擔負單一責任

因此,調度到調度程序:)調度程序的API可能類似於:

public interface IScheduler
{
    event EventHandler<SchedulerEventArgs> Alarm;
    void Start();
    void Stop();
}

現在忘記調度程序。 返回並實現您的第二堂課,這將顯示一些警告。 讓我們先測試(使用Moq):

[Test]
public void ShouldStopDisplayingWarningsWhenTimeIsOut()
{
    Mock<IDisplay> display = new Mock<IDisplay>();
    Mock<IScheduler> scheduler = new Mock<IScheduler>();                      

    Foo foo = new Foo("Bar", scheduler.Object, display.Object);
    scheduler.Raise(s => s.Alarm += null, new SchedulerEventArgs(0));

    display.Verify(d => d.Execute("Bar", WarningState.Ending, null));
    scheduler.Verify(s => s.Stop());
}

編寫實現:

public class Foo
{
    private readonly IScheduler _scheduler;
    private readonly IDisplay _display;
    private readonly string _name;

    public Foo(string name, IScheduler scheduler, IDisplay display)
    {
        _name = name;
        _display = display;
        _scheduler = scheduler;
        _scheduler.Alarm += Scheduler_Alarm;
        _scheduler.Start();
    }

    private void Scheduler_Alarm(object sender, SchedulerEventArgs e)
    {
        _display.Execute(_name, WarningState.Ending, null);
        _scheduler.Stop();
    }
}

測試通過。 寫另一個:

[Test]
public void ShouldNotStopDisplayingWarningsWhenTimeRemains()
{
    Mock<IDisplay> display = new Mock<IDisplay>(MockBehavior.Strict);
    Mock<IScheduler> scheduler = new Mock<IScheduler>(MockBehavior.Strict);
    scheduler.Setup(s => s.Start());

    Foo foo = new Foo("Bar", scheduler.Object, display.Object);
    scheduler.Raise(s => s.Alarm += null, new SchedulerEventArgs(1));
}

測試失敗。 嗯,您需要剩余時間的條件:

private void Scheduler_Alarm(object sender, SchedulerEventArgs e)
{
    if (e.RemainingTime > 0)
        return;

    _display.Execute(_name, WarningState.Ending, null);
    _scheduler.Stop();
}

您可以繼續為您的班級編寫測試,這些測試負責處理調度程序警報並在顯示時執行一些警告。 完成后,您可以為IScheduler接口編寫實現。 通過System.Windows.Forms.Timer或System.ThreadingTimer或其他方式實現調度無關緊要。

是否值得測試該方法? (因為它是私人的)

您的目的是確定您的代碼是否有效。 即使是私有方法,它也應該生成公共接口可以訪問的輸出。 您應該以用戶可以知道其是否正常工作的方式設計類。

同樣,在進行單元測試時,如果可以模擬計時器,則可以訪問分配給計時器的Elapsed事件的回調。

我該如何測試? (我知道我可以讓我的測試類繼承要測試的類...)

您可以在此處使用適配器類。 首先,由於Timer類不提供抽象,因此您必須定義一個抽象。

public interface ITimer
{
    void Start();
    void Stop();
    double Interval { get; set; }
    event ElapsedEventHandler Elapsed;
    //and other members you need
}

然后,您可以在適配器類中實現此接口,而僅從Timer類繼承即可。

public class TimerAdaper : Timer, ITimer { }

您應該將抽象注入構造函數(或作為屬性),以便可以在測試中對其進行模擬。

public class MyClass
{
    private readonly ITimer _timer;

    public MyClass(ITimer timer)
    {
        _timer = timer
    }
}

我應該嘲笑我的計時器嗎? 因為如果我必須測試使用內部計時器的類,那么我的測試可能會花費很多時間才能完成,對嗎?

當然,您應該嘲笑您的計時器。 您的單元測試不能取決於系統時間。 您應該通過模擬引發事件,並查看代碼的行為。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM