2009-11-20 25 views
5

Tôi đang nhúng các ngón chân vào cách thử nghiệm các công cụ đa luồng, nhưng không chắc chắn cách bắt đầu. Tôi chắc rằng tôi sẽ tìm những thứ nhiều hơn ngoài dễ dàng hơn nếu tôi chỉ có thể nhận được thứ đang diễn ra, vì vậy tôi đã tự hỏi nếu ai đó có thể giúp tôi viết một trường hợp thử nghiệm NUnit cho lớp này đơn giản:C#: Làm thế nào để kiểm tra một lớp công nhân luồng cơ bản

class Worker 
{ 
    public event EventHandler<EventArgs> Done = (s, e) => { }; 

    public void StartWork() 
    { 
     var thread = new Thread(Work) { Name = "Worker Thread" }; 
     thread.Start(); 
    } 

    private void Work() 
    { 
     // Do some heavy lifting 
     Thread.Sleep(500); 
     Done(this, EventArgs.Empty); 
    } 
} 

Những gì tôi muốn kiểm tra chỉ đơn giản là: Sự kiện Done được nâng lên khi nó kết thúc. Tôi sẽ không có vấn đề nếu nó được đồng bộ, nhưng không chắc chắn nơi để thậm chí bắt đầu khi nó không phải là. Một thử nghiệm đơn giản nếu nó không phải là đa luồng (và phương pháp Work không phải là riêng tư) có thể là:

[TestFixture] 
class WorkerTests 
{ 
    [Test] 
    public void DoWork_WhenDone_EventIsRaised() 
    { 
     var worker = new Worker(); 

     var eventWasRaised = false; 
     worker.Done += (s, e) => eventWasRaised = true; 

     worker.Work(); 
     Assert.That(eventWasRaised); 
    } 
} 

Bất kỳ con trỏ nào?

Trả lời

7

Bạn cần sử dụng ManualResetEvent - xem Unit Testing Multi-Threaded Asynchronous Events để biết thêm chi tiết.

Cái gì như:

[Test] 
public void DoWork_WhenDone_EventIsRaised() 
{ 
    var worker = new Worker(); 

    var eventWasRaised = false; 
    var mre = new ManualResetEvent(false); 
    worker.Done += (s, e) => { eventWasRaised= true; mre.Set(); }; 

    worker.Work(); 
    mre.WaitOne(1000); 
    Assert.That(eventWasRaised); 
} 
+2

eventRaisedFlag có thể được bỏ qua. Việc sử dụng sự kiện làm cờ là đơn giản hơn. Assert.That (mre.WaitOne (1000)); –

+0

Oooh, giờ đây thông minh ... rực rỡ! @Vadmyst: làm thế nào điều đó sẽ hoạt động nếu nói sự kiện được quản lý để kết thúc trước khi bạn gọi WaitOne? – Svish

+0

Ồ, tôi cho rằng đó là lý do tại sao bạn nên sử dụng ManualResetEvent thay vì AutoResetEvent? – Svish

1

Vấn đề chính bạn tìm thấy với các ứng dụng thử nghiệm luồng đang thực sự kích thích các chủ đề với dữ liệu thử nghiệm bởi vì bạn sẽ cần phải chặn trên các chủ đề chính để chờ cho đến khi thoát thread khác.

Cách chúng tôi đã làm việc với điều này là kiểm tra đồng bộ như bạn đề xuất. Điều này cho phép bạn kiểm tra hành vi hợp lý nhưng nó sẽ không phát hiện deadlocks và điều kiện chủng tộc của khóa học (không phải là thử nghiệm có thể khẳng định những điều này một cách dễ dàng anyway).

1

Bạn có thể sử dụng mẫu chung để hiển thị việc tạo chuỗi cho lớp bên ngoài.

Trong lớp giải nén tạo thread phương pháp ảo:

class Worker 
{ 
    public event EventHandler<EventArgs> Done = (s, e) => { }; 

    public void StartWork() 
    { 
     var thread = CreateThread(); 
     thread.Start(); 
    } 

    // Seam for extension and testability 
    virtual protected Thread CreateThread() 
    { 
     return new Thread(Work) { Name = "Worker Thread" }; 
    } 

    private void Work() 
    { 
     // Do some heavy lifting 
     Thread.Sleep(500); 
     Done(this, EventArgs.Empty); 
    } 
} 

Xác định sub-class đó cho thấy nhiều thread:

class WorkerForTest : Worker 
{ 
    internal Thread thread; 

    protected override Thread CreateThread() 
    { 
     thread = base.CreateThread(); 
     return thread; 
    } 
} 

Đồng bộ hóa các thử nghiệm với các chủ đề:

[TestFixture] 
class WorkerTests 
{ 
    [Test] 
    public void DoWork_WhenDone_EventIsRaised() 
    { 
     var worker = new WorkerForTest(); 

     var eventWasRaised = false; 
     worker.Done += (s, e) => eventWasRaised = true; 

     worker.StartWork(); 

     // Use the seam for synchronizing the thread in the test 
     worker.thread.Join(); 
     Assert.That(eventWasRaised); 
    } 
} 

Trường hợp thiết kế này cho tính khả thi có ưu điểm hơn việc đồng bộ hóa chủ đề thử nghiệm bằng puttin g nó để ngủ trước khi khẳng định:

  • Nó sẽ không có lỗi âm sai như có thể là khi đưa chủ đề thử nghiệm ngủ cho khoảng thời gian thường là đủ cho công nhân chủ đề để kết thúc.
  • Nó sẽ không chạy chậm hơn vì phải mất thời gian ngủ để đảm bảo. Điều này rất quan trọng khi nhiều bài kiểm tra trong bộ phụ thuộc vào nó.
1

Có thể có hai lựa chọn ở đây: 1) Thêm một phương pháp Chờ cho người lao động để bạn có thể chờ đợi hoàn thành 2) Thay vì đối tượng sự kiện boolean sử dụng đơn giản (AutoResetEvent)

Nói chung, mỗi đợi phải đợi thời gian chờ được chỉ định. Trong các mẫu dưới đây chờ đợi là vô hạn.

Đầu tiên Lựa chọn:

class Worker 
{ 
//... 
    Thread thread; 

    public void StartWork() 
    { 
     thread = new Thread(Work) { Name = "Worker Thread" }; 
     thread.Start(); 
    } 

    void WaitCompletion() 
    { 
    if (thread != null) thread.Join(); 
    } 
//... 
} 

[TestFixture] 
class WorkerTests 
{ 
    [Test] 
    public void DoWork_WhenDone_EventIsRaised() 
    { 
     var worker = new Worker(); 

     var eventWasRaised = false; 
     worker.Done += (s, e) => eventWasRaised = true; 

     worker.Work(); 
     worker.WaitCompletion(); 

     Assert.That(eventWasRaised); 
    } 
} 

Thứ hai tùy chọn: (Chờ có thể được thực hiện với thời gian chờ)

[TestFixture] 
class WorkerTests 
{ 
    [Test] 
    public void DoWork_WhenDone_EventIsRaised() 
    { 
     var worker = new Worker(); 

     AutoResetEvent eventWasRaised = new AutoResetEvent(false); 
     worker.Done += (s, e) => eventWasRaised.Set(); 

     worker.Work(); 
     Assert.That(eventWasRaised.WaitOne()); 
    } 
} 
Các vấn đề liên quan