Oke - Saya tidak yakin apakah yang berikut ini akan membantu Anda, karena saya membuat beberapa asumsi dalam mengembangkan solusi yang mungkin atau mungkin tidak benar dalam kasus Anda. Mungkin "solusi" saya terlalu teoretis dan hanya berfungsi untuk contoh buatan - saya belum melakukan pengujian di luar hal-hal di bawah ini.
Selain itu, saya akan melihat solusi berikut lebih dari solusi nyata tetapi mengingat kurangnya tanggapan saya pikir itu mungkin masih lebih baik daripada tidak sama sekali (saya terus menonton pertanyaan Anda menunggu solusi, tetapi tidak melihat satu diposting saya mulai bermain sekitar dengan masalah ini).
Tetapi cukup mengatakan: Katakanlah kita memiliki layanan data sederhana yang dapat digunakan untuk mengambil integer:
public interface IDataService
{
Task<int> LoadMagicInteger();
}
Implementasi sederhana menggunakan kode asinkron:
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;
}
}
Sekarang, muncul masalah, jika kita menggunakan kode "salah" seperti yang diilustrasikan oleh kelas ini. Foo
salah mengakses Task.Result
alih-alih await
hasil seperti Bar
halnya:
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;
}
}
Yang kami (Anda) butuhkan sekarang adalah cara untuk menulis tes yang berhasil ketika memanggil Bar
tetapi gagal saat memanggil Foo
(setidaknya jika saya mengerti pertanyaan dengan benar ;-)).
Saya akan membiarkan kode berbicara; inilah yang saya hasilkan (menggunakan tes Visual Studio, tetapi harus bekerja menggunakan NUnit juga):
DataServiceMock
memanfaatkan TaskCompletionSource<T>
. Ini memungkinkan kita untuk mengatur hasil pada titik yang ditentukan dalam uji coba yang mengarah ke tes berikut. Perhatikan bahwa kami menggunakan delegasi untuk mengembalikan kembali TaskCompletionSource ke pengujian. Anda mungkin juga memasukkan ini ke dalam metode Inisialisasi pengujian dan gunakan properti.
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;
Apa yang terjadi di sini adalah bahwa kami terlebih dahulu memverifikasi bahwa kami dapat meninggalkan metode tanpa memblokir (ini tidak akan berfungsi jika seseorang mengakses Task.Result
- dalam hal ini kami akan mengalami batas waktu karena hasil tugas tidak tersedia sampai setelah metode telah kembali ).
Kemudian, kita mengatur hasilnya (sekarang metode dapat dijalankan) dan kami memverifikasi hasilnya (di dalam unit test kita dapat mengakses Task.Result karena kita benar - benar ingin pemblokiran terjadi).
Kelas tes lengkap - BarTest
berhasil dan FooTest
gagal seperti yang diinginkan.
[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;
}
}
Dan kelas pembantu kecil untuk menguji kebuntuan / batas waktu:
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
artikel orang ini ?