ตกลง - ฉันไม่แน่ใจว่าสิ่งต่อไปนี้จะช่วยคุณได้หรือไม่เพราะฉันได้ตั้งสมมติฐานบางอย่างในการพัฒนาวิธีแก้ปัญหาซึ่งอาจเป็นจริงหรือไม่จริงในกรณีของคุณ บางที "การแก้ปัญหา" ของฉันอาจมีเหตุผลทางทฤษฎีและใช้ได้กับตัวอย่างที่เป็นสิ่งประดิษฐ์เท่านั้น - ฉันยังไม่ได้ทำการทดสอบใด ๆ นอกเหนือจากสิ่งที่อยู่ด้านล่าง
นอกจากนี้ฉันจะเห็นวิธีแก้ปัญหาต่อไปนี้มากกว่าวิธีแก้ปัญหาจริง แต่พิจารณาการขาดการตอบสนองฉันคิดว่ามันอาจจะดีกว่าไม่มีอะไรเลย (ฉันคอยดูคำถามของคุณรอวิธีแก้ปัญหา แต่ไม่เห็นโพสต์ที่เริ่มโพสต์ รอบกับปัญหา)
แต่พอกล่าวว่า: สมมติว่าเรามีบริการข้อมูลอย่างง่ายซึ่งสามารถใช้เพื่อดึงข้อมูลจำนวนเต็ม:
public interface IDataService
{
Task<int> LoadMagicInteger();
}
การใช้งานง่ายใช้รหัสไม่ตรงกัน:
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;
}
}
ตอนนี้มีปัญหาเกิดขึ้นถ้าเราใช้รหัส "ไม่ถูกต้อง" ตามที่แสดงในคลาสนี้ Foo
เข้าถึงอย่างไม่ถูกต้องTask.Result
แทนที่จะawait
ใช้ผลลัพธ์เช่น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;
}
}
สิ่งที่เรา (คุณ) ต้องการคือวิธีเขียนข้อสอบที่ประสบความสำเร็จเมื่อโทรBar
แต่ล้มเหลวเมื่อโทรFoo
(อย่างน้อยถ้าฉันเข้าใจคำถามอย่างถูกต้อง ;-))
ฉันจะให้โค้ดพูด นี่คือสิ่งที่ฉันคิดขึ้นมา (โดยใช้การทดสอบ Visual Studio แต่ควรทำงานโดยใช้ NUnit ด้วย):
DataServiceMock
TaskCompletionSource<T>
Utilizes สิ่งนี้ช่วยให้เราสามารถกำหนดผลลัพธ์ ณ จุดที่กำหนดในการทดสอบการทำงานซึ่งนำไปสู่การทดสอบต่อไปนี้ โปรดทราบว่าเรากำลังใช้ผู้รับมอบสิทธิ์เพื่อส่งกลับ TaskCompletionSource กลับสู่การทดสอบ คุณอาจวางสิ่งนี้ลงในวิธีการเตรียมใช้งานของการทดสอบและใช้คุณสมบัติ
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;
สิ่งที่เกิดขึ้นที่นี่คือเราตรวจสอบก่อนว่าเราสามารถออกจากวิธีการโดยไม่มีการปิดกั้น (จะไม่ทำงานถ้ามีคนเข้าถึงTask.Result
- ในกรณีนี้เราจะหมดเวลาเนื่องจากผลลัพธ์ของงานไม่สามารถใช้ได้จนกว่าหลังจากวิธีการคืน )
จากนั้นเราตั้งค่าผลลัพธ์ (ตอนนี้วิธีสามารถใช้งานได้) และเราตรวจสอบผลลัพธ์ (ในการทดสอบหน่วยที่เราสามารถเข้าถึง Task.Result ตามที่เราต้องการการบล็อกเกิดขึ้นจริง)
ทำคลาสทดสอบเสร็จสมบูรณ์ - BarTest
สำเร็จและFooTest
ล้มเหลวตามต้องการ
[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;
}
}
และชั้นเรียนตัวช่วยเล็ก ๆ เพื่อทดสอบการหยุดชะงัก / หมดเวลา
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
หรือยัง?