Я пытаюсь протестировать некоторые классы, которые полагаются на задачу для выполнения некоторых фоновых вычислений (получение данных из сетевого расположения). Класс получает незапущенный экземпляр Задачи, добавляет метод ContinueWith и затем вызывает Start для Задачи. Примерно так:
private void Search()
{
Task<SearchResults> searchTask = m_searchProvider.GetSearchTask(m_searchCriteria);
searchTask.ContinueWith(OnSearchTaskCompleted);
searchTask.Start();
}
Класс получает экземпляр Task с помощью интерфейса, поэтому я могу добавить экземпляр Task, которым управляет мой тест. Однако я не могу создать тот, над которым у меня достаточно контроля.
Я не хочу вводить потоки в тест, но все же хочу, чтобы Задача велась асинхронно, поэтому я попытался написать класс, реализующий шаблон BeginInvoke / EndInvoke без потоков, и использовать TaskFactory .FromAsync метод для создания Задачи.
Идея состоит в том, что тест может вызывать метод в классе, который запускает задачу, а затем, когда он возвращает тест, может предоставить данные результата в объект Async, который завершает операцию, оставаясь в том же потоке.
Однако, когда я пытаюсь вызвать Start для этой задачи, я получаю сообщение об ошибке, в котором говорится, что «Start не может быть вызван для задачи с нулевым действием». К сожалению, Google не сильно помогает мне в этом сообщении, поэтому я не уверен, правильно ли я реализовал свой объект Async или неправильно использую TaskFactory.FromAsync. Вот мой код для моих классов NonThreadedAsync и теста, который взрывается с исключением:
public class NonThreadedAsync<TResult>
{
private NonThreadedAsyncResult<TResult> m_asyncResult;
public IAsyncResult BeginInvoke(
AsyncCallback callback,
object state)
{
m_asyncResult = new NonThreadedAsyncResult<TResult>(callback, state);
return m_asyncResult;
}
public TResult EndInvoke(IAsyncResult asyncResult)
{
return m_asyncResult.GetResults();
}
public void Complete(TResult data)
{
m_asyncResult.CompleteAsync(data);
}
}
public class NonThreadedAsyncResult<TResult> : IAsyncResult
{
private readonly AsyncCallback m_asyncCallback;
private readonly object m_state;
private readonly ManualResetEvent m_waitHandle;
private bool m_isCompleted;
private TResult m_resultData;
public NonThreadedAsyncResult(AsyncCallback asyncCallback, object state)
{
m_asyncCallback = asyncCallback;
m_state = state;
m_waitHandle = new ManualResetEvent(false);
m_isCompleted = false;
}
public void CompleteAsync(TResult data)
{
m_resultData = data;
m_isCompleted = true;
m_waitHandle.Set();
if (m_asyncCallback != null)
{
m_asyncCallback(this);
}
}
public TResult GetResults()
{
if (!m_isCompleted)
{
m_waitHandle.WaitOne();
}
return m_resultData;
}
#region Implementation of IAsyncResult
public bool IsCompleted
{
get { return m_isCompleted; }
}
public WaitHandle AsyncWaitHandle
{
get { return m_waitHandle; }
}
public object AsyncState
{
get { return m_state; }
}
public bool CompletedSynchronously
{
get { return false; }
}
#endregion
}
[TestClass]
public class NonThreadedAsyncTests
{
[TestMethod]
public void TaskFactoryFromAsync_CanStartReturnedTask()
{
NonThreadedAsync<int> async = new NonThreadedAsync<int>();
Task<int> task = Task<int>.Factory.FromAsync(async.BeginInvoke, async.EndInvoke, null);
task.Start();
}
}
В качестве дополнительной информации, если я отлаживаю этот тест, непосредственно перед тем, как он вызывает Start (), экземпляр задачи отображается в окне Locals следующим образом:
Id = 1, Status = WaitingForActivation, Method = "{null}", Result = "{Not yet computed}"
но в видимых свойствах нет свойства Method, если я его разверну.
Кто-нибудь может увидеть, что я делаю не так?
[Edit: я также написал тест, который подтверждает, что класс NonThreadedAsync правильно работает с классическим шаблоном Begin / End (или, по крайней мере, my пониманием шаблона Begin / End :)) и это проходит:
[TestMethod]
public void NonThreadedAsync_ClassicAccessPattern()
{
int result = 0;
bool asyncCompleted = false;
NonThreadedAsync<int> async = new NonThreadedAsync<int>();
async.BeginInvoke(asyncResult =>
{
result = async.EndInvoke(asyncResult);
asyncCompleted = true;
},
null);
Assert.IsFalse(asyncCompleted);
Assert.AreEqual(0, result);
async.Complete(54);
Assert.IsTrue(asyncCompleted);
Assert.AreEqual(54, result);
}