C# 如何在MVVM中单元测试调用异步方法的DelegateCommand

C# 如何在MVVM中单元测试调用异步方法的DelegateCommand,c#,unit-testing,mvvm,prism,delegatecommand,C#,Unit Testing,Mvvm,Prism,Delegatecommand,我对单元测试MVVM和在项目中使用PRISM还不熟悉。我正在我们当前的项目上实现单元测试,但我并没有幸运地在网上找到可以告诉我如何测试调用异步方法的DelegateCommand的资源。这是我关于如何在MVVM中对异步方法进行单元测试的文章的后续问题,并且回答说可以使用异步TestMethod测试公共方法。只有当我想要测试的方法是公共方法时,这个场景才会起作用 问题是我想测试我的DelegateCommand,因为这是我想在其他类上公开的唯一公共细节,其他所有内容都是私有的。我可以公开我的私有方

我对单元测试MVVM和在项目中使用PRISM还不熟悉。我正在我们当前的项目上实现单元测试,但我并没有幸运地在网上找到可以告诉我如何测试调用异步方法的DelegateCommand的资源。这是我关于如何在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
                }
            }
        }
现在我的测试方法是

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

目前我得到的结果是我的ViewModel.SimpleTask=null这是因为它不等待异步方法完成。

我编写了一个AsyncCommand类,该类从执行方法返回任务对象。然后,您需要显式实现
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();
    }
}
在单元测试中,您只需等待
Execute
方法:

[TestMethod]
public async Task TestAsyncCommand()
{
    var viewModel = new ViewModel();

    Assert.IsFalse(viewModel.Executed);
    await viewModel.AsyncCommand.Execute();

    Assert.IsTrue(viewModel.Executed);
}
另一方面,UI将调用显式实现的
ICommand.Execute
方法,该方法负责等待任务


(*)同时我注意到,如果遵循常见的命名约定,任务返回方法实际上应该命名为
ExecuteAsync

,在Prism 6中,您可以从异步处理程序创建
DelegateCommand
DelegateCommand

例如:

startParsingCommand=DelegateCommand
.FromAsyncHandler(StartPassingAsync、CanStartPassing)

.观测属性(()=>IsParserStarted)

由于我无法添加注释,为了完整起见,在PRISM 6中,您可以尝试:

ParsingCommand = new DelegateCommand<string>(async (x) => await StartParsing(x));
ParsingCommand=newdelegatecommand(异步(x)=>wait StartParsing(x));

FromAsyncHandler在Prism 6.x中已经过时(我不知道是哪个x:D),所以有没有更好的方法来实现这一点?这并不能回答问题。问题是如何测试执行
async
处理程序的
DelegateCommand
,而不是如何创建这样的命令。
[TestMethod]
public async Task TestAsyncCommand()
{
    var viewModel = new ViewModel();

    Assert.IsFalse(viewModel.Executed);
    await viewModel.AsyncCommand.Execute();

    Assert.IsTrue(viewModel.Executed);
}
ParsingCommand = new DelegateCommand<string>(async (x) => await StartParsing(x));