5

I am not sure where to start but let me give you a brief idea on where I am and what I want to achieve. I am quite new to Unit Testing on MVVM and having difficulty on testing the commands that I exposed using PRISM delegate command properties. My delegate commands calls async method that has to be waited so I can get the actual result. Below is an asyc method that is called by method that I wanted to test.

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

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

Currently what I am getting is that my ViewModel.SimpleTask = null this is because it does not wait for the async method to finish. I understand there are some related topics to this already in stack overflow but I could not find something related to my DelegateCommands.

Julien
  • 353
  • 3
  • 11
Jepoy_D_Learner
  • 435
  • 1
  • 6
  • 13

1 Answers1

10

Your method GetTasksAsync should return a Task so you can actually wait for it.

I recommend this series on Channel9 an specially this episode explaining your problem.

Just to make is clear: Simply changing the signature of GetTasksAsync to

Task GetTasksAsync();

allows you to do this:

var t = GetAsync();
t.Wait();
Assert(...);

In case you really want to test the command in your unit tests and not the actually method called by the command you can use a field in your ViewModel to store the task to await (not so clean) or replace your DelegateCommand by something like described in this post: Awaitable DelegateCommand

Update: In addition to the blog post and considering you are using PRISM, you should have a look a Project Kona from the same team as PRISM. They actually implemented DelegateCommand to support AsyncHandlers

Julien
  • 353
  • 3
  • 11
  • 3
    Or even better, this allows you to use an `async Task` unit test and do `await GetTasksAsync()`. – Stephen Cleary Mar 26 '13 at 12:18
  • Thanks for the answer.However this GetTasksAsync() is a private method within my Viewmodel and not wanting to expose to any other objects within my project and is only called by Delegates. Having said that I am only testing Public Methods. These delegates are exposed as public. Introducing another field could be a way to go but you are right it will be dirty. I will look at the Awaitable Delegate COmmand link that you sent me and see if it will be useful. – Jepoy_D_Learner Mar 26 '13 at 23:27
  • Update: I implemented your above answer for the fact that I could not yet workout other alternate for the solution- e.g. Awaitable DelegateCommand. Implementing this means I changed my private methods to public(this smells!), now my problem is that when GetTaskAsync() is called by using t.Wait() this throw InnerException = {"This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread."} . This because it adds items into my collection from the other thread. Any idea? – Jepoy_D_Learner Mar 27 '13 at 04:14
  • 1
    From .NET 4.5 you can use BindingOperations.EnableCollectionSynchronization Method (check MSDN or this blog post http://www.jonathanantoine.com/2011/09/24/wpf-4-5-part-7-accessing-collections-on-non-ui-threads/ ) – Julien Mar 27 '13 at 07:41
  • 1
    Thanks for your answer. I will make your answer as the answer to this question since I did not asked the spot on question to what I really need as it really wan not really known during the time. As an update I have posted this new question as a follow up question to this post http://stackoverflow.com/questions/15727515/how-to-unit-test-delegatecommand-that-calls-async-methods-in-mvvm. – Jepoy_D_Learner Mar 31 '13 at 07:55