本文介绍了如何单元测试DelegateCommand调用在MVVM异步方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是新来的单元测试MVVM和使用我的项目PRISM。我对我们目前的项目执行单元测试和没有运气找到在线资源,将告诉我怎么totest DelegateCommand调用异步方法。这是我的岗位跟进的问题 - How单元测试与异步方法的视图模型。在MVVM异步方法以及如何进行单元测试得到的回答是公共方法可以使用​​异步TestMethod的测试。此方案将只工作,如果我想测试方法是公共方法。

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 tell me how totest DelegateCommand that calls async method. This is a follow up question to my post - How to Unit Test a ViewModel with async method. on how to unit test an async methods in MVVM and was answered that public methods can be tested using async TestMethod. This scenario will work only if the method that I want to test are public methods.

问题是我想测试我的DelegateCommand,因为这是我要揭露其他类和其他一切都是保密的唯一公开的细节。我可以公开我的私有方法为公共但我绝不会做这种作为一个不好的设计。我不知道如何去这个问题 - 是DelegateCommand需要进行测试,或者有一些其他的解决此问题?我有兴趣知道其他去了解这一点,并以某种方式导致我在正确的道路。

The problem is I want to test my DelegateCommand as this are the only public details that I want to expose on other classes and everything else are private. I can expose my private methods as public but I will never do this as its a bad design. I am not sure on how to go about this - Is DelegateCommand needs to be tested, or there are some other work around this? I am interested to know how other go about this and somehow lead me to the right path.

下面是我的codeS再次

Here are my codes again

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

也在这里是我的VM调用上述

also here is the command in my VM that calls the async method above

  this.GetTasksCommand = new DelegateCommand(this.GetTasks);
      void GetTasks()
        {
                GetTasksAsync();
        }

现在我的测试方法是这样

and now my Test Method goes like

 [TestMethod]
        public void Command_Test_GetTasksCommand()
        {
          MyViewModel.GetTaskCommand.Execute(); // this should populate ViewModel.SimpleTask 
          Assert.IsTrue(MyBiewModel.SimpleTask != null)
        } 

目前我所得到的是我的ViewModel.SimpleTask = NULL,这是因为它不等待异步方法来完成。

Currently what I am getting is that my ViewModel.SimpleTask = null this is because it does not wait for the async method to finish.

推荐答案

我写了返回从执行方法的任务对象的AsyncCommand类。然后,您需要实现 ICommand.Execute 明确,从等待任务的执行实施

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

用户界面,在另一方面,将调用明确实施 ICommand.Execute 方法,需要等待任务的照顾。

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

(*)在我注意到,如果你遵循通用命名约定,任务返回的方法实际上应该被命名为其间 ExecuteAsync

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

这篇关于如何单元测试DelegateCommand调用在MVVM异步方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-27 14:16