2013-06-05 44 views
7

Tôi đang sử dụng Moq để kiểm tra đơn vị một số mã bao gồm vòng lặp Parallel.foreach.Tại sao một thử nghiệm đơn vị .NET bao gồm một phụ thuộc phần cứng Parallel.foreach?

Giai đoạn Sắp xếp thiết lập 4 ngoại lệ để được ném trong vòng lặp và sau đó được bao bọc trong AggregateException.

Điều này thông qua bộ vi xử lý i7 của tôi và tôi đã kiểm tra mã.

Sau đó, một đồng nghiệp phàn nàn rằng nó không được chuyển cho anh ấy. Hóa ra là Parallel.foreach chỉ sinh ra 2 luồng trên Core2duo của mình trước khi ném bom và do đó chỉ có 2 ngoại lệ được bọc trong AggregateException.

Câu hỏi đặt ra là phải làm gì với điều này để kiểm tra đơn vị không phụ thuộc vào kiến ​​trúc bộ vi xử lý? Một vài suy nghĩ: -

  1. Có một Microsoft article trên tay thêm ngoại lệ đối với các AggregateException nhưng chúng tôi không quan tâm về việc này như vòng lặp nên thoát vào dịp sớm nhất nếu có một vấn đề.
  2. ParallelOptions.MaxDegreeOfParallelism có thể đặt giới hạn trên cho số lượng chủ đề được sử dụng. Nhưng trừ khi điều này được bật xuống đến 1 (trong đó có vẻ giống như gian lận hơn kiểm tra đơn vị thích hợp) cách có thể kiểm tra đơn vị biết bao nhiêu đề thực sự sẽ được sử dụng và do đó thiết lập Sắp xếpKhẳng định giai đoạn propertly?
+2

Số lượng ngoại lệ con không phải là yêu cầu và không được kiểm tra theo cách này. Đó là một chi tiết thực hiện. Bạn có thể kiểm tra 1+ ngoại lệ của loại chính xác. –

+0

@HenkHolterman Đúng nhưng tôi phải thiết lập mô hình phù hợp. Bạn có đề nghị tôi nên thiết lập cho số lượng tối đa các cuộc gọi giả có thể nhưng không khẳng định rằng tất cả những cuộc gọi này thực sự được gọi vào cuối? –

+0

Tôi không chắc chắn phần nào bạn muốn thử nhưng nó không quan trọng. –

Trả lời

6

Bạn không nên kiểm tra thứ gì đó như thế - đó là chi tiết triển khai.

Thing là - Parallel.ForEach sẽ xử lý các phần tử cho đến khi ngoại lệ. Khi ngoại lệ xảy ra, nó sẽ ngừng xử lý bất kỳ phần tử mới nào (nhưng sẽ xử lý xong các phần tử hiện đang được xử lý) và sau đó ném AgregateException.

Hiện tại - CPU i7 của bạn có 4 lõi + Siêu luồng, dẫn đến nhiều luồng hơn để xử lý, do đó bạn có thể nhận được nhiều ngoại lệ hơn (vì ví dụ 4 điều có thể được xử lý cùng lúc khi ngoại lệ xảy ra). Nhưng trên Core2Duo chỉ với 2 lõi, chỉ có 2 mục sẽ được xử lý cùng một lúc (đó là vì TPL đủ thông minh để tạo ra chỉ đủ luồng để xử lý, không nhiều hơn lõi có sẵn).

Kiểm tra thực tế 4 ngoại lệ đã xảy ra cho bạn không có kiến ​​thức. Nó phụ thuộc vào máy. Thay vào đó, bạn nên thử nghiệm nếu có ít nhất một ngoại lệ xảy ra - vì đó là những gì bạn đang mong đợi. Nếu người dùng trong tương lai sẽ chạy mã của bạn trên máy lõi đơn cũ, anh ta sẽ chỉ nhận được một ngoại lệ này trong AggregateException.

Số lượng ngoại lệ được ném là máy cụ thể, ví dụ như thời gian tính toán ví dụ - bạn sẽ không xác định thời gian tính toán, vì vậy bạn không nên xác nhận số ngoại lệ trong trường hợp này.

+2

BTW, nó không chỉ là về CPU. Các hành vi cụ thể cũng có thể thay đổi tùy thuộc vào những gì khác đang chạy trên ThreadPool trong quá trình tương tự, những gì các quá trình khác đang sử dụng CPU, cho dù các hoạt động trong vòng lặp là ngăn chặn và có thể những thứ khác. – svick

+0

Vâng, điều đó hoàn toàn đúng. Mối quan hệ CPU chỉ là mối quan hệ dễ nhất, nhưng tất cả những gì bạn đề cập cũng hợp lệ. – Pako

+0

@Pako Cảm ơn - câu trả lời của bạn giải thích vấn đề rất tốt. Như tôi đã hiểu, giải pháp của bạn là thiết lập mô hình để cho phép số lượng ngoại lệ tối đa (= chủ đề) nhưng sau đó không thực sự khẳng định rằng tất cả chúng đều đã xảy ra. Điều này có vẻ hơi bất thường trong đó bình thường khi chế giễu tôi đang sử dụng cho tất cả các thiết lập được kiểm chứng. –

1

Kiểm tra đơn vị xác minh rằng những gì bạn đang mong đợi xảy ra và những gì thực sự xảy ra là giống nhau. Điều đó có nghĩa là bạn cần phải tự hỏi mình bạn đang mong đợi điều gì sẽ xảy ra.

Nếu bạn mong đợi rằng tất cả các lỗi tiềm năng trong quá trình xử lý sẽ được báo cáo, khi đó đồng nghiệp của bạn chạy thực sự đã tìm thấy lỗi trong mã của bạn.Điều này có nghĩa là đồng bằng Parallel.ForEach() sẽ không hoạt động cho bạn, nhưng một cái gì đó như Parallel.ForEach() với try - catch và xử lý ngoại lệ tùy chỉnh sẽ.

Nếu những gì bạn mong đợi là mỗi ném ngoại lệ sẽ bị bắt, thì đó là những gì bạn cần kiểm tra. Điều này có thể làm phức tạp kiểm tra của bạn hoặc có thể không thể kiểm tra, tùy thuộc vào cách bạn ném các ngoại lệ. Một xét nghiệm đơn giản cho điều này sẽ trông như thế này:

[Test] 
public void ParallelForEachExceptionsTest() 
{ 
    var exceptions = new ConcurrentQueue<Exception>(); 
    var thrown = Assert.Throws<AggregateException>(
     () => Parallel.ForEach(
      Enumerable.Range(1, 4), i => 
      { 
       var e = new Exception(i.ToString()); 
       exceptions.Enqueue(e); 
       throw e; 
      })); 

    CollectionAssert.AreEquivalent(exceptions, thrown.InnerExceptions); 
} 

Nếu những gì bạn mong đợi là khi một hoặc nhiều trường hợp ngoại lệ được ném, sau đó ít nhất một vài trong số họ sẽ bị bắt, thì đó là những gì bạn nên thử nghiệm, và bạn không nên lo lắng nếu tất cả các trường hợp ngoại lệ đã bị bắt một cách chính xác.

+0

Rất cám ơn vì những nỗ lực của bạn ở đây. Đó là trường hợp thứ hai - mỗi * ném * ngoại lệ phải được đánh bắt. Các ngoại lệ được ném thực sự đến từ một cuộc gọi phương thức trên một đối tượng giả. Vì vậy, như bạn nói, tôi chỉ cần xác minh rằng một tập hợp con trong số này thực sự được ném. –