Unit Testing an MVVM RelayCommand

The RelayCommand is an excellent way to communicate between a view and View Model in MVVM without creating a link to a specific instance of either. However; one problem that I recently encountered when using these was: how are they unit tested.

Take the following piece of code (taken from https://tfsutilities.codeplex.com/):

    public class MainViewModel : ViewModelBase
    {          
        public RelayCommand FindOrphanedWorkspaceCommand { get ; private set ; }

This is instantiated here:

         public MainViewModel( IDataService dataService)
        {
            ...

            FindOrphanedWorkspaceCommand = new RelayCommand(FindOrphanedWorkspaces);

And, finally, the method itself:

         /// <summary>
        /// Call method to retrieve all workspaces that currently have no pending changes
        /// </summary>
        private void FindOrphanedWorkspaces()
        {
            // Doesn't really matter what this actually does

So; can we just write a test; what about this:

    [TestClass]
    public class MainViewModelTest
    {
        [ TestMethod]
        public void FindOrphanedWorkspaces()
        {
            IDataService ds = new Mock.MockDataService ();
            MockMainViewModel mvm = new MockMainViewModel (ds);

            mvm.FindOrphanedWorkspaceCommand();

        }

The answer, of course, is no (otherwise this wouldn’t be a particularly useful blog post).

The Workaround

There is a workaround (which is pretty much by fallback workaround these days when I find something can’t be unit tested for reasons of protection):

Simply change the private method to protected and subclass the viewmodel:

    class MockMainViewModel : MainViewModel
    {
        public MockMainViewModel( IDataService dataService) : base (dataService) { }

         public void FindOrphanedWorkspaces()
        {
            base.FindOrphanedWorkspaces();

This certainly works and, in its defence, it tests as completely as calling the RelayCommand (I don’t believe testing the MVVM architecture is within the remit of the test architecture of any dependent program).

However…

It feels like a lot of additional work (as it happens, it’s work you may have to do anyway for other things, but that’s beside the point). So, what’s the alternative?

RelayCommand implements ICommand, here’s the metadata:

namespace GalaSoft.MvvmLight.Command
{
    // Summary:
    //     A command whose sole purpose is to relay its functionality to other objects
    //     by invoking delegates. The default return value for the CanExecute method
    //     is 'true'. This class does not allow you to accept command parameters in
    //     the Execute and CanExecute callback methods.
    public class RelayCommand : ICommand
    {
        // Summary:
        //     Initializes a new instance of the RelayCommand class that can always execute.
        //
        // Parameters:
        //   execute:
        //     The execution logic.
        //
        // Exceptions:
        //   System.ArgumentNullException:
        //     If the execute argument is null.
        public RelayCommand( Action execute);
        //
        // Summary:
        //     Initializes a new instance of the RelayCommand class.
        //
        // Parameters:
        //   execute:
        //     The execution logic.
        //
        //   canExecute:
        //     The execution status logic.
        //
        // Exceptions:
        //   System.ArgumentNullException:
        //     If the execute argument is null.
        public RelayCommand( Action execute, Func< bool> canExecute);

        // Summary:
        //     Occurs when changes occur that affect whether the command should execute.
        public event EventHandler CanExecuteChanged;

        // Summary:
        //     Defines the method that determines whether the command can execute in its
        //     current state.
        //
        // Parameters:
        //   parameter:
        //     This parameter will always be ignored.
        //
        // Returns:
        //     true if this command can be executed; otherwise, false.
        public bool CanExecute( object parameter);
        //
        // Summary:
        //     Defines the method to be called when the command is invoked.
        //
        // Parameters:
        //   parameter:
        //     This parameter will always be ignored.
        public virtual void Execute( object parameter);
        //
        // Summary:
        //     Raises the GalaSoft.MvvmLight.Command.RelayCommand.CanExecuteChanged event.
        public void RaiseCanExecuteChanged();
    }
}

Consequently, you can simply do this:

        [ TestMethod]
        public void FindOrphanedWorkspaces()
        {
            IDataService ds = new Mock.MockDataService ();
            MockMainViewModel mvm = new MockMainViewModel (ds);

            mvm.FindOrphanedWorkspaceCommand.Execute( null);

        }

And, there’s more. If you’ve implemented the CanExecute, you can test if that works; for example:

Assert.IsTrue(mvm.FindOrphanedWorkspaceCommand.CanExecute(null));

Or

Assert.IsFalse(mvm.FindOrphanedWorkspaceCommand.CanExecute(null));

In my case, as it stands, the former.

Conclusion

So, we can check whether the command can execute, and whether it does execute. Admittedly this isn’t ground-breaking research, but it took me longer that it should have to figure it out, and next time, I’ll have a blog post to refer to.

2 thoughts on “Unit Testing an MVVM RelayCommand

  1. Tick

    Why would you unit test a framework element? That makes no sense… Are we testing that the entire .NET framework works? Just test the underlying methods and don’t worry about testing the relaycommand (icommand)…

    Reply
    1. pcmichaels Post author

      Thanks for your comment. You raise a good question. It comes down to the argument around testing functionality over testing public interfaces.

      Personally, I think that you should attempt to test the public interface of your functionality (as this makes for a less brittle test); and in the case of a command, the command represents the public interface.

      Whilst the RelayCommand itself is, indeed, part of a framework that (as you rightly say) is not your job to test, it does provide the tooling to test your own code in a more reliant manner. Resilient because, should you decide that the button on the screen now does something else, you can bind a different method to it, and your tests will still act on the command.

      Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.