Ok - Tôi không chắc liệu những điều sau đây có giúp ích gì cho bạn không, vì tôi đã đưa ra một số giả định trong việc phát triển một giải pháp có thể đúng hoặc không đúng trong trường hợp của bạn. Có lẽ "giải pháp" của tôi quá lý thuyết và chỉ hoạt động đối với các ví dụ nhân tạo - Tôi chưa thực hiện bất kỳ thử nghiệm nào ngoài những thứ bên dưới.
Ngoài ra, tôi sẽ thấy một cách giải quyết sau đây hơn là một giải pháp thực sự nhưng xem xét việc thiếu phản hồi Tôi nghĩ rằng nó vẫn có thể tốt hơn không có gì (Tôi cứ xem câu hỏi của bạn chờ đợi một giải pháp, nhưng không thấy một câu hỏi nào được đăng lên tôi bắt đầu chơi xung quanh với vấn đề).
Nhưng đủ để nói: giả sử chúng ta có một dịch vụ dữ liệu đơn giản có thể được sử dụng để truy xuất một số nguyên:
public interface IDataService
{
Task<int> LoadMagicInteger();
}
Một triển khai đơn giản sử dụng mã không đồng bộ:
public sealed class CustomDataService
: IDataService
{
public async Task<int> LoadMagicInteger()
{
Console.WriteLine("LoadMagicInteger - 1");
await Task.Delay(100);
Console.WriteLine("LoadMagicInteger - 2");
var result = 42;
Console.WriteLine("LoadMagicInteger - 3");
await Task.Delay(100);
Console.WriteLine("LoadMagicInteger - 4");
return result;
}
}
Bây giờ, một vấn đề phát sinh, nếu chúng ta đang sử dụng mã "không chính xác" như được minh họa bởi lớp này. Foo
truy cập không chính xác Task.Result
thay vì await
ing kết quả như Bar
:
public sealed class ClassToTest
{
private readonly IDataService _dataService;
public ClassToTest(IDataService dataService)
{
this._dataService = dataService;
}
public async Task<int> Foo()
{
var result = this._dataService.LoadMagicInteger().Result;
return result;
}
public async Task<int> Bar()
{
var result = await this._dataService.LoadMagicInteger();
return result;
}
}
Những gì chúng tôi (bạn) bây giờ cần là một cách để viết một bài kiểm tra thành công khi gọi Bar
nhưng không thành công khi gọi Foo
(ít nhất là nếu tôi hiểu chính xác câu hỏi ;-)).
Tôi sẽ để mã nói; Đây là những gì tôi đã đưa ra (sử dụng các bài kiểm tra Visual Studio, nhưng nó cũng hoạt động bằng NUnit):
DataServiceMock
tận dụng TaskCompletionSource<T>
. Điều này cho phép chúng tôi đặt kết quả tại một điểm được xác định trong quá trình chạy thử dẫn đến thử nghiệm sau. Lưu ý rằng chúng tôi đang sử dụng một đại biểu để trả lại TaskCompletionSource trở lại thử nghiệm. Bạn cũng có thể đặt nó vào phương thức Khởi tạo của các thuộc tính kiểm tra và sử dụng.
TaskCompletionSource<int> tcs = null;
this._dataService.LoadMagicIntegerMock = t => tcs = t;
Task<int> task = null;
TaskTestHelper.AssertDoesNotBlock(() => task = this._instance.Foo());
tcs.TrySetResult(42);
var result = task.Result;
Assert.AreEqual(42, result);
this._end = true;
Điều xảy ra ở đây là trước tiên chúng tôi xác minh rằng chúng tôi có thể rời khỏi phương thức mà không chặn (điều này sẽ không hoạt động nếu có ai đó truy cập Task.Result
- trong trường hợp này chúng tôi sẽ hết thời gian chờ vì kết quả của nhiệm vụ không khả dụng cho đến khi phương thức được trả về ).
Sau đó, chúng tôi đặt kết quả (bây giờ phương thức có thể thực thi) và chúng tôi xác minh kết quả (bên trong một bài kiểm tra đơn vị, chúng tôi có thể truy cập vào Task.Result vì chúng tôi thực sự muốn chặn xảy ra).
Hoàn thành lớp kiểm tra - BarTest
thành công và FooTest
thất bại như mong muốn.
[TestClass]
public class UnitTest1
{
private DataServiceMock _dataService;
private ClassToTest _instance;
private bool _end;
[TestInitialize]
public void Initialize()
{
this._dataService = new DataServiceMock();
this._instance = new ClassToTest(this._dataService);
this._end = false;
}
[TestCleanup]
public void Cleanup()
{
Assert.IsTrue(this._end);
}
[TestMethod]
public void FooTest()
{
TaskCompletionSource<int> tcs = null;
this._dataService.LoadMagicIntegerMock = t => tcs = t;
Task<int> task = null;
TaskTestHelper.AssertDoesNotBlock(() => task = this._instance.Foo());
tcs.TrySetResult(42);
var result = task.Result;
Assert.AreEqual(42, result);
this._end = true;
}
[TestMethod]
public void BarTest()
{
TaskCompletionSource<int> tcs = null;
this._dataService.LoadMagicIntegerMock = t => tcs = t;
Task<int> task = null;
TaskTestHelper.AssertDoesNotBlock(() => task = this._instance.Bar());
tcs.TrySetResult(42);
var result = task.Result;
Assert.AreEqual(42, result);
this._end = true;
}
}
Và một lớp người trợ giúp nhỏ để kiểm tra sự bế tắc / thời gian chờ:
public static class TaskTestHelper
{
public static void AssertDoesNotBlock(Action action, int timeout = 1000)
{
var timeoutTask = Task.Delay(timeout);
var task = Task.Factory.StartNew(action);
Task.WaitAny(timeoutTask, task);
Assert.IsTrue(task.IsCompleted);
}
}
async
bài viết của anh chàng này ?