2016-01-14 30 views
5

Chúng tôi đang viết các bài kiểm tra đơn vị cho mã không đồng bộ sử dụng MSTest và Moq.Mocking Async Methods

Vì vậy, chúng tôi có một số mã trông giống như sau:

var moq = new Mock<Foo>(); 
moq.Setup(m => m.GetAsync()) 
    .Returns(Task.FromResult(10)); 

Hoặc như thế này trên các dự án đó có một phiên bản mới hơn của Moq

var moq = new Mock<Foo>(); 
moq.Setup(m => m.GetAsync()) 
    .ReturnsAsync(10); 

Nhìn vào thực hiện Moq của ReturnsAsync:

public static IReturnsResult<TMock> ReturnsAsync<TMock, TResult>(this IReturns<TMock, Task<TResult>> mock, TResult value) where TMock : class 
{ 
    TaskCompletionSource<TResult> completionSource = new TaskCompletionSource<TResult>(); 
    completionSource.SetResult(value); 
    return mock.Returns(completionSource.Task); 
} 

Cả hai phương pháp đều có vẻ giống nhau. Cả hai tạo một số TaskCompletionSource, gọi SetResult và trả lại Task

Cho đến giờ rất tốt.

Nhưng các phương pháp ngắn hạn async được tối ưu hóa để hoạt động đồng bộ. Điều này dường như ngụ ý rằng TaskCompletionSource luôn đồng bộ, điều này dường như cũng gợi ý rằng việc xử lý bối cảnh và mọi vấn đề liên quan có thể xảy ra sẽ không bao giờ xảy ra.

Vì vậy, nếu chúng ta có một số mã đã được thực hiện một số async không-không, như trộn awaits, Wait()Result, rằng những vấn đề này sẽ không được phát hiện trong kiểm tra đơn vị.

Có lợi thế nào để tạo phương pháp tiện ích mở rộng luôn kiểm soát được không? Một cái gì đó như:

public async Task<T> ReturnsYieldingAsync<T>(T result) 
{ 
    await Task.Yield(); 
    return result; 
} 

Trong trường hợp này, chúng tôi sẽ có phương pháp được đảm bảo thực thi không đồng bộ.

Lợi thế nhận thức sẽ phát hiện mã không đồng bộ không hợp lệ. Ví dụ, nó có thể bắt bất kỳ deadlocking hoặc ngoại lệ nuốt trong thử nghiệm đơn vị.

Tôi không chắc chắn 100% trường hợp này, vì vậy tôi thực sự muốn nghe những gì cộng đồng nói.

+0

Tôi muốn nói đến 'ReturnsYieldingAsync', nhưng thậm chí nhiều hơn thế, để trải nghiệm deadlocks tiềm năng, bạn cần phải cài đặt bối cảnh đồng bộ hóa trong runners thử nghiệm của bạn, giống như ['AsyncPump'] (http: // blogs. msdn.com/b/pfxteam/archive/2012/01/20/10259049.aspx). – Noseratio

+0

Tôi nghĩ bạn nên thử viết một test case chứng minh 1. nó sẽ hoạt động như mong đợi và 2. bạn có thể tạo test test theo trường hợp test 1 chính xác nhưng không bao giờ gọi 'ReturnsYieldingAsync' và thất bại. –

+1

@Noseratio Rất dễ tìm trên 'AsyncPump'. Vấn đề chính tôi nghĩ rằng chúng tôi đang chạy vào là thử nghiệm chạy vào là bối cảnh đồng bộ hóa của thử nghiệm là vô giá trị. Hoán đổi 'AsyncPump' có vẻ là cách để đi như được hiển thị ở đây http: // stackoverflow.com/questions/14087257/how-to-add-sync-context-to-async-test-method – swestner

Trả lời

2

Khi lần đầu tiên tôi bắt đầu talking about testing asynchronous code bốn năm trước (!), Tôi sẽ khuyến khích các nhà phát triển thử nghiệm dọc theo trục không đồng bộ cho mocks của họ. Tức là, kiểm tra dọc theo trục kết quả (thành công, thất bại) cũng như trục không đồng bộ (đồng bộ, không đồng bộ).

Theo thời gian, mặc dù, tôi đã nới lỏng điều này. Khi nói đến việc thử nghiệm mã của riêng tôi, tôi thực sự chỉ kiểm tra các đường dẫn thành công/thất bại đồng bộ, trừ khi có một lý do rõ ràng để kiểm tra đường dẫn thành công không đồng bộ cho mã đó. Tôi không bận tâm với thử nghiệm thất bại không đồng bộ nữa.

+0

Trong trường hợp của chúng tôi, chúng tôi đã đi qua một số trường hợp của các mẫu không đồng bộ hỗn hợp. Phần lớn đây là một vấn đề đào tạo, nhưng nó cũng là một cái gì đó chúng ta không thể bỏ qua. Thư viện Nito của bạn đã giúp chúng tôi tái tạo các bế tắc một cách nhất quán. Cảm ơn! Ultimatley mặc dù chúng tôi có thể sẽ kiểm tra các loại vấn đề này thông qua các bài kiểm tra E2E. – swestner

+0

Tôi có một sự tò mò nổi bật là cốt lõi đối với câu hỏi ban đầu. Bạn có biết nếu 'TaskCompletionSource' luôn chạy đồng bộ không? Đó là một giả định của câu hỏi ban đầu mà sẽ là tốt đẹp để biết. – swestner

+0

@swestner: Nếu bạn gọi 'SetResult' (hoặc tương tự), thì nhiệm vụ được hoàn thành trước khi trả về cuộc gọi phương thức. Tuy nhiên, vẫn có thể tiếp tục công việc đó mà chưa hoàn thành. –