Ok-귀하의 경우에 맞거나 맞지 않을 수있는 솔루션을 개발할 때 약간의 가정을했기 때문에 다음이 귀하에게 도움이 될지 확실하지 않습니다. 어쩌면 내 "솔루션"이 너무 이론적이며 인공적인 예에서만 작동합니다. 아래 항목을 넘어서는 테스트를 수행하지 않았습니다.
또한 실제 솔루션보다 다음 해결 방법을 더 많이 볼 수 있지만 응답 부족을 고려하면 여전히 아무것도 아닌 것보다 낫다고 생각합니다 (문제를 기다리는 동안 질문을 계속 보았지만 게시 된 것을 보지 못했습니다) 문제와 함께).
그러나 충분히 말했다 : 정수를 검색하는 데 사용할 수있는 간단한 데이터 서비스가 있다고 가정 해 봅시다.
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>
합니다. 이를 통해 테스트 실행의 정의 된 지점에서 결과를 설정하여 다음 테스트로 이어질 수 있습니다. 우리는 델리게이트를 사용하여 TaskCompletionSource를 다시 테스트로 전달합니다. 이것을 테스트의 Initialize 메소드에 넣고 속성을 사용할 수도 있습니다.
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
기사 모음 을 읽었 습니까?