Frage

I have a class (for simplicity I'll call it MyCustomCommand) which basically accepts two Delegates for Execute and CanExecute. This class implements ICommand. This then allows me to declare a property in my view model which I bind to from XAML.

The problem is I've run into a scenario where I need to Mock the view model due to a dependency in one of my commands called ApplyChangesCommand. I'm not convinced the dependency is a bad thing yet. It's relatively necessary at this point.

Because of this dependency, I'm using Mock Setups to create a Callback which basically "does nothing" to circumvent the dependency.

Now that I've Mock'd the view model, any instance properties are of course now null. This includes my commands.

Simple example would be:

private void _somethingToExecute;
public ICommand ApplyChangesCommand { get { return MyCustomCommand(_somethingToExecute, e=>true); }

Is there any way with Mock that I can actually invoke ApplyChangesCommand's _somethingToExecute? Callbase doesn't cut it and I can't think of any other way to do it.

One workaround is to make "_somethingToExecute" public and create the ApplyChangesCommand in my test, but I'm not a fan.

Any advice appreciated.

Thanks

War es hilfreich?

Lösung

Ok, now that I've had more time to look at this not being rushed at work, I see the issue. What you should do is use an injected factory to create the ApplyChangesCommand. Then for unit testing your VM, you just verify that the command returns the factory created command. Below is an example:

public class MyViewModel
{
    private MyCustomCommandFactory _commandFactory;
    private void _somethingToExecute;

    public MyViewModel(MyCustomCommandFactory commandFactory)
    {
        _commandFactory = commandFactory;
    }

    public ICommand ApplyChangesCommand  
    { 
        get 
        { 
            return _commandFactory.Create(_somethingToExecute, e=>true);
        }
    }
}

This assumes you're wanting to create a new command every time the get is called (which seems to be how you have it set up). If you want just a single command created for the life of the VM, obviously you can just create the command via the factory in the VM constructor.

To unit test this, you can do something like this:

[Test]
public void ApplyChangesCommand_Always_ReturnsFactoryCreatedCommand
{
    Mock<ICommand> mockCreatedCustomCommand = new Mock<ICommand>();
    Mock<MyCustomCommandFactory> mockCommandFactory = new Mock<MyCustomCommandFactory>();
    mockCreatedCustomCommand.Setup(p => p.Create(It.IsAny<Action>(), e => true))
                            .Returns(mockCreatedCustomCommand.Object);
    Assert.That(systemUnderTest.ApplyChangesCommand, Is.SameAs(mockCreatedCustomCommand.Object));
}

(Change Action out for whatever your delegate actually is).

This is all you should have to unit test for a VM's command. If you're wanting to test that the behavior is what you expect (meaning, it executes the passed in delegate and returns the expected value using that delegate), then that's the realm of an acceptance tests.

Note: I used the Moq mocking framework syntax in my test example.

Personally, I wouldn't be passing in delegates like that to a Command. I would instead inject whatever the Command needs and have all the logic inside the Command, where it is more easily unit tested in there, and has a looser dependency on the VM.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top