Как выполнить модульный тест DelegateCommand, который вызывает асинхронные методы в MVVM
Я новичок в модульном тестировании MVVM и использую PRISM в моем проекте. Я внедряю модульное тестирование в нашем текущем проекте, и мне не повезло найти ресурсы в Интернете, которые бы рассказывали мне, как протестировать DelegateCommand, который вызывает асинхронный метод. Это следующий вопрос к моему посту - Как выполнить модульное тестирование ViewModel с помощью асинхронного метода. о том, как выполнить модульное тестирование асинхронных методов в MVVM, и ответили, что публичные методы могут быть протестированы с использованием асинхронного TestMethod. Этот сценарий будет работать, только если метод, который я хочу протестировать, является общедоступным.
Проблема в том, что я хочу протестировать мой DelegateCommand, так как это единственные открытые данные, которые я хочу раскрыть в других классах, а все остальное является приватным. Я могу выставлять свои частные методы как публичные, но я никогда не буду делать это как плохой дизайн. Я не уверен, как это сделать - нужно ли тестировать DelegateCommand или есть еще какие-то решения? Мне интересно знать, как другие идут по этому поводу и каким-то образом ведут меня на правильный путь.
Вот мои коды снова
async void GetTasksAsync()
{
this.SimpleTasks.Clear();
Func<IList<ISimpleTask>> taskAction = () =>
{
var result = this.dataService.GetTasks();
if (token.IsCancellationRequested)
return null;
return result;
};
IsBusyTreeView = true;
Task<IList<ISimpleTask>> getTasksTask = Task<IList<ISimpleTask>>.Factory.StartNew(taskAction, token);
var l = await getTasksTask; // waits for getTasksTask
if (l != null)
{
foreach (ISimpleTask t in l)
{
this.SimpleTasks.Add(t); // adds to ViewModel.SimpleTask
}
}
}
также здесь есть команда в моей виртуальной машине, которая вызывает асинхронный метод выше
this.GetTasksCommand = new DelegateCommand(this.GetTasks);
void GetTasks()
{
GetTasksAsync();
}
и теперь мой метод испытаний идет как
[TestMethod]
public void Command_Test_GetTasksCommand()
{
MyViewModel.GetTaskCommand.Execute(); // this should populate ViewModel.SimpleTask
Assert.IsTrue(MyBiewModel.SimpleTask != null)
}
В настоящее время я получаю то, что мой ViewModel.SimpleTask = null это потому, что он не ожидает завершения асинхронного метода.
3 ответа
Я написал класс AsyncCommand, который возвращает объект Task из Execute
метод. Затем вам нужно реализовать ICommand.Execute
явно, в ожидании Задачи от вашего Execute
реализация:
public class AsyncCommand : ICommand
{
public event EventHandler CanExecuteChanged;
public Func<Task> ExecutedHandler { get; private set; }
public Func<bool> CanExecuteHandler { get; private set; }
public AsyncCommand(Func<Task> executedHandler, Func<bool> canExecuteHandler = null)
{
if (executedHandler == null)
{
throw new ArgumentNullException("executedHandler");
}
this.ExecutedHandler = executedHandler;
this.CanExecuteHandler = canExecuteHandler;
}
public Task Execute()
{
return this.ExecutedHandler();
}
public bool CanExecute()
{
return this.CanExecuteHandler == null || this.CanExecuteHandler();
}
public void RaiseCanExecuteChanged()
{
if (this.CanExecuteChanged != null)
{
this.CanExecuteChanged(this, new EventArgs());
}
}
bool ICommand.CanExecute(object parameter)
{
return this.CanExecute();
}
async void ICommand.Execute(object parameter)
{
await this.Execute();
}
}
Затем вы можете передать асинхронные методы, возвращающие задачи, классу команд:
public class ViewModel
{
public AsyncCommand AsyncCommand { get; private set; }
public bool Executed { get; private set; }
public ViewModel()
{
Executed = false;
AsyncCommand = new AsyncCommand(Execute);
}
private async Task Execute()
{
await(Task.Delay(1000));
Executed = true;
}
}
В ваших юнит-тестах вы просто ждете Execute
метод:
[TestMethod]
public async Task TestAsyncCommand()
{
var viewModel = new ViewModel();
Assert.IsFalse(viewModel.Executed);
await viewModel.AsyncCommand.Execute();
Assert.IsTrue(viewModel.Executed);
}
Интерфейс, с другой стороны, будет вызывать явно реализованный ICommand.Execute
метод, который заботится о ожидании задачи.
(*) В то же время я заметил, что если вы следуете общим соглашениям об именах, метод, возвращающий Задачу, должен фактически называться ExecuteAsync
,
Поскольку для полноты картины я не могу добавлять комментарии, в PRISM 6 вы можете попробовать:
ParsingCommand = new DelegateCommand<string>(async (x) => await StartParsing(x));
В Prism 6 вы можете создать DelegateCommand
а также DelegateCommand<T>
из асинхронного обработчика.
Например:
startParsingCommand=DelegateCommand
.FromAsyncHandler(StartParsingAsync,CanStartParsing)
.ObservesProperty(()=> IsParserStarted);