如何对在 MVVM 中调用异步方法的 DelegateCommand 进行单元测试 [英] How to Unit Test DelegateCommand that calls async methods in MVVM

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

问题描述

我不熟悉单元测试 MVVM 并在我的项目中使用 PRISM.我正在对我们当前的项目实施单元测试,但在网上找不到资源来告诉我如何测试调用异步方法的 DelegateCommand.这是我的帖子的后续问题 - 如何进行单元测试带有异步方法的 ViewModel. 关于如何在 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.

这里又是我的代码

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

现在我的测试方法就像

 [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 类,它从 Execute 方法返回一个 Task 对象.然后你需要显式地实现 ICommand.Execute,等待来自你的 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;
    }
}

在您的单元测试中,您只需等待 Execute 方法:

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

另一方面,UI 将调用显式实现的 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.

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

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆