如何在测试(视图模型)class 中对方法调用进行单元测试?

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

我使用 xUnitMoq

在我的 MainViewModel class 中,我有一个由两个命令调用的方法。该方法更新了几个属性: `

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

我只是想避免为每个命令测试整个方法的更新。第一次执行命令:

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

    CommandCompletedControlsSetup(); // here is method call
}

第二次命令执行:

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

    CommandCompletedControlsSetup(); // here is method call
}

所以不要这样做两次:

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

我想做这样的事情:

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

但是 MainViewModel 没有被模拟,我不确定我是否能够模拟它?如果是,那么如何?如果没有,是否有其他方法可以测试调用了 VM 方法?还是我应该将其保留为显而易见的东西并自行测试该方法?

模拟和依赖倒置原则齐头并进。如果你想模拟一些东西,你需要 'invert the dependency' 模拟目标,这样你就可以用实际的模拟对象实例替换具体实现。在您的具体示例中,您需要将 CommandCompletedControlsSetup() 提取到单独的 class 中,并使用构造函数注入将其传递给视图模型。由于视图模型需要无参数构造函数才能在 XAML 中对其进行实例化,这将导致应用程序发生重大变化 start-up。我不知道你现在是怎么做的,但你需要 bootstrap 你的应用程序并让你的 DI 容器创建视图实例并手动显示。

您的另一个选择是单独使用这些命令,不要显式测试它们。您的命令由一组方法组成。一种方法要精确(另一种方法是 CommandCompletedControlsSetup() 并且您不想测试)。
在您的两个示例中,您的命令都会调用服务 class:
的 public 方法 updateDataService.CancelUpdates();_simulateDataService.CancelSimulation();.
所以,您可能已经测试过此服务。如果不是,您可以或应该测试它们。如果你测试了这个服务,你也隐含地测试了你的视图模型命令,因为它们依赖于那些服务。这样你绕过了 CommandCompletedControlsSetup().

但总的来说,您想要模拟的每个行为以及您想要测试的类型的一部分,都必须注入到该类型中。

附带说明一下,我个人更喜欢使用转换器将我的控件的可见性绑定到我的视图模型公开的布尔值 属性。您可以使用 .NET 框架提供的 BooleanToVisibilityConverter 转换器。这样我就避免了对查看相关程序集的引用。