bakunet
bakunet

Reputation: 197

How to unit test calling of method in tested (view model) class?

I use xUnit and Moq.

In my MainViewModel class I have a method that is called by two commands. The method updates several properties: `

public void CommandCompletedControlsSetup()
{
    //TokenSource.Dispose();
    UpdateStatusBar = 0;
    VisibilityStatusBar = Visibility.Hidden;
    ValidateButtons();
    ProgressDisplay = "";
    WorkStatus = "";
    VisibilityCancellingMsg = Visibility.Visible;
    VisibilityCancelTestingBtn = Visibility.Collapsed;
    VisibilityTestingBtn = Visibility.Visible;
    VisibilityCancelUpdatingBtn = Visibility.Collapsed;
    VisibilityUpdatingBtn = Visibility.Visible;
}

And I just wanted to avoid testing update of the whole method for each command. First command execution:

public void OnUpdateCancellationExecute(object obj)
{
    _updateDataService.CancelUpdates();

    CommandCompletedControlsSetup(); // here is method call
}

Second command execution:

public void OnSimulateCancellationExecute(object obj)
{
    _simulateDataService.CancelSimulation();

    CommandCompletedControlsSetup(); // here is method call
}

So instead of doing this twice:

[Fact]
public void OnSimulateCancellationExecute_UpdatesViewProps_True()
{
    _viewModel.UpdateStatusBar = 1000;
    _viewModel.VisibilityStatusBar = Visibility.Visible;
    _viewModel.ProgressDisplay = "1000/1000";
    _viewModel.WorkStatus = "some status";
    _viewModel.VisibilityCancellingMsg = Visibility.Hidden;
    _viewModel.VisibilityCancelTestingBtn = Visibility.Visible;
    _viewModel.VisibilityTestingBtn = Visibility.Hidden;
    _viewModel.VisibilityCancelUpdatingBtn = Visibility.Visible;
    _viewModel.VisibilityUpdatingBtn = Visibility.Hidden;

    _viewModel.SimulateCancellationCommand.Execute(null);

    Assert.Equal(0, _viewModel.UpdateStatusBar);
    Assert.Equal(Visibility.Hidden, _viewModel.VisibilityStatusBar);
    Assert.Equal("", _viewModel.ProgressDisplay);
    Assert.Equal("", _viewModel.WorkStatus);
    Assert.Equal(Visibility.Visible, _viewModel.VisibilityCancellingMsg);
    Assert.Equal(Visibility.Collapsed, _viewModel.VisibilityCancelTestingBtn);
    Assert.Equal(Visibility.Visible, _viewModel.VisibilityTestingBtn);
    Assert.Equal(Visibility.Collapsed, _viewModel.VisibilityCancelUpdatingBtn);
    Assert.Equal(Visibility.Visible, _viewModel.VisibilityUpdatingBtn);
}

I wanted to do something like this:

[Fact]
        public void CancellationExecuteMethods_UpdatesViewProps_True()
        {
            _viewModel.SimulateCancellationCommand.Execute(null);

            _viewModel.UpdateCancellationCommand.Execute(null);

            //MainViewModel does not contain definition for Verify - exception
            _viewModel.Verify(sd => sd.CancelUpdates(), Times.Exactly(2));
        }

BUT the MainViewModel is not mocked and I am not sure that I am able to mock it? If yes, then how? If not, is there any alternative to test that VM method was called? Or should I just leave it as something obvious and just test the method by itself?

Upvotes: 1

Views: 415

Answers (1)

BionicCode
BionicCode

Reputation: 29028

Mocking and Dependency Inversion Principle go hand in hand. If you want to mock something you need to 'invert the dependency' of the mocking target, so that you can replace a concrete implementation wit an actual mocked object instance. In your concrete example you would need to extract CommandCompletedControlsSetup() into a separate class and pass it to the view model using constructor injection. Since the view model needs a parameterless constructor in order to instantiate it in XAML, this would lead to significant changes to the application start-up. I don't know how you are doing it right now, but you would need to bootstrap your application and let your DI container create the view instances and show the manually.

Another option you have is to leave this commands alone and don't test them explicitly. Your commands are composed of a set of methods. One method to be precise (the other method is CommandCompletedControlsSetup() and this you don't want to test).
In both of your examples your commands invoke a public method of a service class:
updateDataService.CancelUpdates(); and _simulateDataService.CancelSimulation();.
So, you probably already tested this services. If not the you could or should test them. If you tested this services, you also implicitly tested your view model commands, since they depend on those services. This way you stepped around CommandCompletedControlsSetup().

But in general, every behaviour you would like to mock and that is part of the type you want to test, must be injected into that type.

As a side note, I personally prefer to bind my control's visibility to a boolean property exposed by my view models using a converter. You can use the BooleanToVisibilityConverter converter provided by the .NET framework. This way I avoid references to view related assemblies.

Upvotes: 1

Related Questions