How to Unit Test DelegateCommand that calls async methods in MVVM

前端 未结 3 1133
無奈伤痛
無奈伤痛 2020-12-09 11:30

I am new to Unit Testing MVVM and using PRISM on my project. I am implementing Unit Testing on our current project and not having luck finding resources online that would te

相关标签:
3条回答
  • 2020-12-09 11:45

    In Prism 6, you can create DelegateCommand and DelegateCommand<T> from async handler.

    For example:

    startParsingCommand=DelegateCommand .FromAsyncHandler(StartParsingAsync,CanStartParsing) .ObservesProperty(()=> IsParserStarted);

    0 讨论(0)
  • 2020-12-09 11:50

    Since I cannot add comments, for completeness sake, in PRISM 6 you could try:

    ParsingCommand = new DelegateCommand<string>(async (x) => await StartParsing(x));
    
    0 讨论(0)
  • 2020-12-09 12:00

    I wrote an AsyncCommand class that returns a Task object from the Execute method. You then need to implement ICommand.Execute explicitly, awaiting the Task from your Execute implementation:

    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();
        }
    }
    

    You can then pass async Task-returning methods to the command class:

    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;
        }
    }
    

    In your unit tests, you simply await the Execute method:

    [TestMethod]
    public async Task TestAsyncCommand()
    {
        var viewModel = new ViewModel();
    
        Assert.IsFalse(viewModel.Executed);
        await viewModel.AsyncCommand.Execute();
    
        Assert.IsTrue(viewModel.Executed);
    }
    

    The UI, on the other hand, will call the explicitly implemented ICommand.Execute method which takes care of awaiting the task.

    (*) In the meantime I noticed that if you follow common naming conventions, the Task-returning method should actually be named ExecuteAsync.

    0 讨论(0)
提交回复
热议问题