2013-06-05 29 views
5

Hiện tại tôi đang trừu tượng hóa khái niệm về bộ tính giờ để lớp học của tôi cần có thể sử dụng bộ tính giờ giả trong các thử nghiệm hoặc triển khai khác nhau trong chế độ tác vụ (ví dụ: bộ hẹn giờ của bộ tạo luồng, bộ hẹn giờ luồng, v.v.). Vì vậy, tôi đã tạo ra giao diện này:Làm thế nào để kiểm tra đơn vị một lớp bộ đếm thời gian (mẫu bộ điều hợp)?

public interface ITimer : IDisposable 
{ 
    bool IsEnabled { get; } 
    bool IsAutoResetting { get; set; } 
    TimeSpan Interval { get; set; } 

    void Start(); 
    void Stop(); 

    event EventHandler IntervalElapsed; 
} 

Bây giờ tôi muốn tạo một wrapper mà điều chỉnh lớp System.Threading.Timer và thực hiện các giao diện đó. Tôi muốn làm điều đó bằng cách sử dụng phát triển theo hướng thử nghiệm. Lớp học của tôi hiện nay có vẻ hơi như thế này:

public sealed class ThreadPoolTimer : ITimer 
{ 
    private readonly Timer _timer; 

    public bool IsEnabled { get; private set; } 

    public bool IsAutoResetting { get; set; } 

    public TimeSpan Interval { get; set; } 

    public ThreadPoolTimer() 
    { 
     Interval = this.GetDefaultInterval(); 
     _timer = new Timer(OnTimerCallback); 
    } 

    public void Dispose() 
    { 
     _timer.Dispose(); 
    } 

    public void Start() 
    { 

    } 

    public void Stop() 
    { 

    } 

    private void OnTimerCallback(object state) 
    { 
     OnIntervalElapsed(); 
    } 

    public event EventHandler IntervalElapsed; 

    private void OnIntervalElapsed() 
    { 
     var handler = IntervalElapsed; 
     if (handler != null) 
      handler(this, EventArgs.Empty); 
    } 
} 

câu hỏi thực tế của tôi là: cách bạn sẽ viết bài kiểm tra đơn vị mô tả (soft real-time) yêu cầu đối với hành vi của Start, StopIntervalElapsed?

Theo tôi, tôi nên sử dụng ví dụ: an AutoResetEvent và kiểm tra xem sự kiện có được nâng lên trong một khoảng thời gian nhất định (có thể là +/- 3ms) hay không. Nhưng viết mã đó có phần vi phạm nguyên tắc DAMP (các cụm từ mô tả và có ý nghĩa), tôi nghĩ vậy. Có cách nào dễ hơn để thực hiện việc này không?

Tôi có nên phụ thuộc vào System.Threading.Timer bên ngoài và sau đó có thể sử dụng shim cho mục đích thử nghiệm không? Thật không may, các bộ đếm thời gian .NET không có một giao diện chung (mà sẽ làm cho công việc của tôi quá cũ ...)

Suy nghĩ của bạn về chủ đề đó là gì? Có tài liệu nào mà tôi chưa tìm thấy và tôi nên đọc không?

Xin lỗi vì đã thực sự có nhiều câu hỏi trong bài đăng này, nhưng việc thử nghiệm các yêu cầu thời gian thực mềm này khá thú vị, tôi nghĩ vậy.

+0

có thể sao chép http://stackoverflow.com/questions/9088313/unit-testing-system-timers-timer – Mzf

+2

Tôi không nghĩ đó là một bản sao bởi vì tất cả các câu hỏi này đề cập đến các lớp học đóng gói một bộ đếm thời gian và làm thế nào để mô phỏng nó nhưng không ai thực hiện giao diện thực tế bằng cách sử dụng TDD. Các câu hỏi khác về vấn đề này là v.d. http: // stackoverflow.com/questions/879971/how-do-you-unit-test-classes-sử dụng-giờ-nội bộ hoặc http://stackoverflow.com/questions/12045/unit-testing-a-timer-based-application – feO2x

+0

tạo ra một wrapper cảm thấy giống như một mùi. Tôi nghĩ rằng các lớp bộ đếm thời gian trong khuôn khổ được đảm bảo để làm việc. Tôi muốn phân lớp để kiểm tra chủ đề để gọi trình xử lý hẹn giờ của tôi hoặc tiêm khoảng thời gian riêng của tôi để viết một vài bài kiểm tra tích hợp. – bryanbcook

Trả lời

0

Vì chưa có ai trả lời câu hỏi này, tôi sẽ cho bạn biết cách tiếp cận vấn đề: Tôi đã sử dụng mẫu gián điệp để thực sự triển khai mã quan sát hành vi của bộ hẹn giờ. Lớp trông như thế này:

public class ThreadPoolTimerSpy : IDisposable 
{ 
    private readonly ThreadPoolTimer _threadPoolTimer; 

    private int _intervalElapsedCallCount; 

    private readonly ManualResetEvent _resetEvent = new ManualResetEvent(false); 

    public int NumberOfIntervals { get; set; } 

    public DateTime StartTime { get; private set; } 
    public DateTime EndTime { get; private set; } 

    public ThreadPoolTimerSpy(ThreadPoolTimer threadPoolTimer) 
    { 
     if (threadPoolTimer == null) throw new ArgumentNullException("threadPoolTimer"); 
     _threadPoolTimer = threadPoolTimer; 
     _threadPoolTimer.IntervalElapsed += OnIntervalElapsed; 
     NumberOfIntervals = 1; 
    } 

    public void Measure() 
    { 
     _intervalElapsedCallCount = 0; 
     _resetEvent.Reset(); 
     StartTime = DateTime.Now; 
     _threadPoolTimer.Start(); 

     _resetEvent.WaitOne(); 
    } 

    private void OnIntervalElapsed(object sender, EventArgs arguments) 
    { 
     _intervalElapsedCallCount++; 

     if (_intervalElapsedCallCount < NumberOfIntervals) 
      return; 

     _threadPoolTimer.Stop(); 
     EndTime = DateTime.Now; 
     _resetEvent.Set(); 
    } 


    public void Dispose() 
    { 
     _threadPoolTimer.Dispose(); 
     _resetEvent.Dispose(); 
    } 
} 

lớp này có một ThreadPoolTimer và đăng ký sự kiện IntervalElapsed của nó. Người ta có thể xác định khoảng thời gian gián điệp nên đợi cho đến khi nó ngừng đo. Khi tôi đang sử dụng ManualResetEvent để chặn chuỗi khởi động bộ hẹn giờ theo phương thức Measure, tất cả các cuộc gọi đến phương thức đó đều đồng bộ, dẫn đến mã DAMP trong lớp thử nghiệm thực tế, theo ý kiến ​​của tôi.

Một phương pháp xét nghiệm sử dụng các điệp viên sẽ trông như thế này:

[TestInitialize] 
public void InitializeTestEnvironment() 
{ 
    _testTarget = new ThreadPoolTimerBuilder().WithAutoResetOption(true) 
               .WithInterval(100) 
               .Build() as ThreadPoolTimer; 
    Assert.IsNotNull(_testTarget); 
    _spy = new ThreadPoolTimerSpy(_testTarget); 
} 

[TestMethod] 
public void IntervalElapsedMustBeRaisedExactlyTenTimesAfter1000Milliseconds() 
{ 
    CheckIntervalElapsed(10, TimeSpan.FromMilliseconds(1000), TimeSpan.FromMilliseconds(100)); 
} 

private void CheckIntervalElapsed(int numberOfIntervals, TimeSpan expectedTime, TimeSpan toleranceInterval) 
{ 
    _spy.NumberOfIntervals = numberOfIntervals; 
    _spy.Measure(); 
    var passedTime = _spy.EndTime - _spy.StartTime; 
    var timingDifference = Math.Abs(expectedTime.Milliseconds - passedTime.Milliseconds); 
    Assert.IsTrue(timingDifference <= toleranceInterval.Milliseconds, string.Format("Timing difference: {0}", timingDifference)); 
} 

Nếu bạn có bất kỳ thắc mắc hoặc kiến ​​nghị, xin vui lòng để lại comment.

Bên cạnh đó: khoảng thời gian khoan dung mà tôi phải chọn để thực hiện các bài kiểm tra là tương đối cao. Tôi nghĩ rằng có lẽ 3 đến 5 mili giây có thể đủ, nhưng cuối cùng trong mười khoảng thời gian tôi đã tìm ra rằng khoảng thời gian đo thực tế là lên đến 72ms khác với thời gian dự kiến ​​1000ms trong trường hợp này. Vâng, không bao giờ sử dụng thời gian chạy được quản lý cho các ứng dụng thời gian thực, tôi đoán ...